This is the second day of my participation in the November Gwen Challenge. Check out the details: the last Gwen Challenge 2021

The plugin can do

  • tostoreAdd new properties
  • definestoreTo add a new option
  • tostoreAdd a new method
  • Existing methods of packaging
  • Change or even cancel operations
  • Implement local storage and other side effects
  • onlyFor a particularstoreapplication

Nature of the plugin

Pinia’s plug-in is essentially a function whose return value is mixed into the Store

Case study:

Import {createPinia} from 'pinia' // Add a secret attribute to all stores function SecretPiniaPlugin() {return {secret: 'the cake is a lie' } } const pinia = createPinia() pinia.use(SecretPiniaPlugin)Copy the code
Const store = useStore() store. Secret // 'The cake is a lie'Copy the code

export interface Pinia { install: Exclude<Plugin['install'], undefined> state: Ref<Record<string, StateTree>> use(plugin: PiniaStorePlugin): Pinia App _testing? : boolean }Copy the code
use(plugin) { if (! localApp) { toBeInstalled.push(plugin) } else { _p.push(plugin) } return this }Copy the code

Looking at the type definition of the source code, we can see that Pinia uses the plug-in through use, which is stored in the _p array of the instance and returns this, the current instance.

The timing of plug-in use

function buildStoreToUse( partialStore: StoreWithState<Id, S, G, A>, descriptor: StateDescriptor<S>, $id: Id, getters: G = {} as G, actions: A = {} as A, options: DefineStoreOptions<Id, S, G, A> ) { ... _p. ForEach ((extender) => {if (__DEV__ &&is_client) {// @ts-expect-error: plugins pinia._p.forEach((extender) => {if (__DEV__ &&is_client) {// @ts-expect-error: conflict between A and ActionsTree const extensions = extender({ store, app: pinia._a, pinia, options }) Object.keys(extensions || {}).forEach((key) => store._customProperties.add(key) ) assign(store, Extensions)} else {// the result returned by the plugin is mixed on the store // @ts-expect-error: conflict between A and ActionsTree assign(store, extender({ store, app: pinia._a, pinia, options })) } }) return store }Copy the code

We can see the application of the plugin in the buildStoreToUse function, and buildStoreToUse happens to be the last step in useStore, where we can get all the properties of the store completely.

We see that the function loops through all the plug-ins for pinia._p, extender is the plug-in we use. The plugin runs at this point, passing the {store, app: pinia. _A, pinia, options} composition argument to our plugin, which is the context our plugin gets. In the end, the plugin’s results are mixed with object.assign.

That is, the plug-in application succeeds. At this point our store’s capacity is increasing.

export function myPiniaPlugin(context) {
  context.pinia // the pinia created with `createPinia()`
  context.app // the current app created with `createApp()` (Vue 3 only)
  context.store // the store the plugin is augmenting
  context.options // the options object defining the store passed to `defineStore()`
  // ...
}
Copy the code

Simple case

We can add attributes to each store by simply returning their objects in the plug-in:

pinia.use(() => ({ hello: 'world' }))
Copy the code

You can also set properties directly on the Store

pinia.use(({ store }) => {
  store.hello = 'world'
})
Copy the code

Any properties returned by the plug-in will be automatically tracked by DevTools, so in order for Hello to be visible in DevTools, make sure store._customProperties is added to dev mode only if you want to debug it in DevTools:

// from the example above
pinia.use(({ store }) => {
  store.hello = 'world'
  // make sure your bundler handle this. webpack and vite should do it by default
  if (process.env.NODE_ENV === 'development') {
    store._customProperties.add('secret')
  }
})
Copy the code