Plugins

Try this lesson on Scrimba

Vuex stores accept the plugins option that exposes hooks for each mutation. A Vuex plugin is simply a function that receives the store as the only argument:

  1. const myPlugin = (store) => {
  2. // called when the store is initialized
  3. store.subscribe((mutation, state) => {
  4. // called after every mutation.
  5. // The mutation comes in the format of `{ type, payload }`.
  6. })
  7. }

And can be used like this:

  1. const store = createStore({
  2. // ...
  3. plugins: [myPlugin]
  4. })

Committing Mutations Inside Plugins

Plugins are not allowed to directly mutate state - similar to your components, they can only trigger changes by committing mutations.

By committing mutations, a plugin can be used to sync a data source to the store. For example, to sync a websocket data source to the store (this is just a contrived example, in reality the createWebSocketPlugin function can take some additional options for more complex tasks):

  1. export default function createWebSocketPlugin (socket) {
  2. return (store) => {
  3. socket.on('data', data => {
  4. store.commit('receiveData', data)
  5. })
  6. store.subscribe(mutation => {
  7. if (mutation.type === 'UPDATE_DATA') {
  8. socket.emit('update', mutation.payload)
  9. }
  10. })
  11. }
  12. }
  1. const plugin = createWebSocketPlugin(socket)
  2. const store = createStore({
  3. state,
  4. mutations,
  5. plugins: [plugin]
  6. })

Taking State Snapshots

Sometimes a plugin may want to receive “snapshots” of the state, and also compare the post-mutation state with pre-mutation state. To achieve that, you will need to perform a deep-copy on the state object:

  1. const myPluginWithSnapshot = (store) => {
  2. let prevState = _.cloneDeep(store.state)
  3. store.subscribe((mutation, state) => {
  4. let nextState = _.cloneDeep(state)
  5. // compare `prevState` and `nextState`...
  6. // save state for next mutation
  7. prevState = nextState
  8. })
  9. }

Plugins that take state snapshots should be used only during development. When using webpack or Browserify, we can let our build tools handle that for us:

  1. const store = createStore({
  2. // ...
  3. plugins: process.env.NODE_ENV !== 'production'
  4. ? [myPluginWithSnapshot]
  5. : []
  6. })

The plugin will be used by default. For production, you will need DefinePlugin for webpack or envify for Browserify to convert the value of process.env.NODE_ENV !== 'production' to false for the final build.

Built-in Logger Plugin

Vuex comes with a logger plugin for common debugging usage:

  1. import { createLogger } from 'vuex'
  2. const store = createStore({
  3. plugins: [createLogger()]
  4. })

The createLogger function takes a few options:

  1. const logger = createLogger({
  2. collapsed: false, // auto-expand logged mutations
  3. filter (mutation, stateBefore, stateAfter) {
  4. // returns `true` if a mutation should be logged
  5. // `mutation` is a `{ type, payload }`
  6. return mutation.type !== "aBlocklistedMutation"
  7. },
  8. actionFilter (action, state) {
  9. // same as `filter` but for actions
  10. // `action` is a `{ type, payload }`
  11. return action.type !== "aBlocklistedAction"
  12. },
  13. transformer (state) {
  14. // transform the state before logging it.
  15. // for example return only a specific sub-tree
  16. return state.subTree
  17. },
  18. mutationTransformer (mutation) {
  19. // mutations are logged in the format of `{ type, payload }`
  20. // we can format it any way we want.
  21. return mutation.type
  22. },
  23. actionTransformer (action) {
  24. // Same as mutationTransformer but for actions
  25. return action.type
  26. },
  27. logActions: true, // Log Actions
  28. logMutations: true, // Log mutations
  29. logger: console, // implementation of the `console` API, default `console`
  30. })

The logger file can also be included directly via a <script> tag, and will expose the createVuexLogger function globally.

Note the logger plugin takes state snapshots, so use it only during development.