Nuxt.JS Vuex State Tree

From WikiOD

For every big project, it is necessary to use the state tree (store) to manage the state (state). This is why Nuxt.js kernel implements Vuex.

Use state tree[edit | edit source]

Nuxt.js will try to find the store directory under the application root directory. If the directory exists, it will do the following:

  1. Reference vuex module
  2. Add the vuex module to the vendors build configuration
  3. Set the store configuration item of the Vue root instance

Nuxt.js supports two ways to use the store, you can choose one of them:

  • Module method: Each .js file in the store directory will be converted into a sub-module named by the state tree  (of course, index is the root module)
  • Classic (not recommended): store/index.js returns the method to create an instance of Vuex.Store.

No matter which mode you use, the value of your state should always be function. In order to avoid returning reference types, multiple instances will affect each other.

Normal way[edit | edit source]

Nuxt.js allows you to have a store directory that contains every file corresponding to the module.

First, just export the state as a function, and export the variables and operations as objects in store/index.js:

export const state = () => ({
   counter: 0
 })
 
 export const mutations = {
   increment (state) {
     state.counter++
   }
 }

Then, you can have the store/todos.js file:

export const state = () => ({
   list: []
 })
 
 export const mutations = {
   add (state, text) {
     state.list.push({
       text,
       done: false
     })
   },
   remove (state, { todo }) {
     state.list.splice(state.list.indexOf(todo), 1)
   },
   toggle (state, todo) {
     todo.done = !todo.done
   }
 }

Vuex will be created as follows:

new Vuex.Store({
   state: () => ({
     counter: 0
   }),
   mutations: {
     increment (state) {
       state.counter++
     }
   },
   modules: {
     todos: {
       namespaced: true,
       state: () => ({
         list: []
       }),
       mutations: {
         add (state, { text }) {
           state.list.push({
             text,
             done: false
           })
         },
         remove (state, { todo }) {
           state.list.splice(state.list.indexOf(todo), 1)
         },
         toggle (state, { todo }) {
           todo.done = !todo.done
         }
       }
     }
   }
 })

In your pages/todos.vue, use the todos module:

<template>
 <nowiki> </nowiki> <nowiki><ul>
     <li v-for="todo in todos">
       <input type="checkbox" :checked="todo.done" @change="toggle(todo)"></nowiki>
 <nowiki> </nowiki>     <nowiki><span :class="{ done: todo.done }">{{ todo.text }}</nowiki><nowiki></span></nowiki>
 <nowiki> </nowiki>   <nowiki></li></nowiki>
 <nowiki> </nowiki>   <nowiki><li><input placeholder="What needs to be done?" @keyup.enter="addTodo"></nowiki><nowiki></li></nowiki>
 <nowiki> </nowiki> <nowiki></ul></nowiki>
 </template>
 
 <nowiki><script>
 import { mapMutations } from 'vuex'
 
 export default {
   computed: {
     todos () {
       return this.$store.state.todos.list
     }
   },
   methods: {
     addTodo (e) {
       this.$store.commit('todos/add', e.target.value)
       e.target.value = ''</nowiki>
 <nowiki> </nowiki>   },
 <nowiki> </nowiki>   ...mapMutations({
 <nowiki> </nowiki>     toggle: 'todos/toggle'
 <nowiki> </nowiki>   })
 <nowiki> </nowiki> }
 }
 <nowiki></script></nowiki>
 
 <nowiki><style>
 .done {
   text-decoration: line-through;
 }
 </style></nowiki></code>
<blockquote>The module approach is also applicable to top-level definitions without the need to implement subdirectories in the store directory</blockquote>Example: You create the file store/state.js and add the following content
 <code>export default () => ({
   counter: 0
 })

Correspondingly, you can add store/mutations.js in the folder

export default {
   increment (state) {
     state.counter++
   }
 }
Module file[edit | edit source]

You can decompose the module file into separate files: state.js, actions.js, mutations.js and getters.js. If you use index.js to maintain state, getters, actions, and mutations, and have a single operation file, you can still identify the file correctly.

Note: When using the split file module, you must remember to use the arrow function function, this is available lexically. The lexical scope this means that it always points to the owner of the referencing arrow function. If the arrow function is not included, then this will be undefined. The solution is to use the "normal" function, which points this to its own scope, so it can be used.

Plug-in[edit | edit source]

You can add other plugins to the store (in module mode) and put them in the store/index.js file:

import myPlugin from 'myPlugin'
 
 export const plugins = [ myPlugin ]
 
 export const state = () => ({
   counter: 0
 })
 
 export const mutations = {
   increment (state) {
     state.counter++
   }
 }

More information about plugins:  Vuex documentation .

fetch method[edit | edit source]

The fetch method will be called before the page is rendered, and its role is to fill in the state tree (store) data. It is similar to the asyncData method, except that it does not set the component data.

For more information about the fetch method, please refer to the  page fetch method API .

nuxS erverInit method[edit | edit source]

If the nuxtServerInit method is specified in the state tree, when Nuxt.js calls it, it will pass the context object of the page as the second parameter to it (only when called by the server). This method is very useful when we want to send some data from the server to the client.

For example, suppose that the currently logged-in user can be accessed through req.session.user in the session state tree of our server. To pass the logged-in user information to the state tree of the client, we only need to update store/index.js as follows:

actions: {
   nuxtServerInit ({ commit }, { req }) {
     if (req.session.user) {
       commit('user', req.session.user)
     }
   }
 }

If you use the state tree modularization mode, only the main module (ie store/index.js) is suitable for setting this method (other modules will not be called if they are set).

At this time, the context is given nuxt ServerInit as the second parameter, which is the same as the async Data or fetch method. The context object received by the nuxt ServerInit method is the same as that of fetch, but does not include context. redirect() and context. error().

Note: Asynchronous nuxt ServerInit operations must return Promises to notify the nuxt server to wait for them.

actions: {
   async nuxtServerInit({ dispatch }) {
     await dispatch('core/load')
   }
 }
Vuex strict mode[edit | edit source]

By default, strict mode is enabled in development mode, and mode is disabled in production mode. To disable strict mode in dev, follow the example below.

Module Mode[edit | edit source]

export const strict = false

Classic mode[edit | edit source]

This feature has been deprecated and will be removed in Nuxt 3.

To create Vuex in classic mode, we should create a store/index.js file with methods that return Vuex instances everywhere:

import Vuex from 'vuex'
 
 const createStore = () => {
   return new Vuex.Store({
     strict: false,
     state: () => ({
       counter: 0
     }),
     mutations: {
       increment (state) {
         state.counter++
       }
     }
   })
 }
 
 export default createStore

We don't need to install it because Vuex is provided by Nuxt.js.

We can now use this. $store in our component:

<template>
 <nowiki> </nowiki> <nowiki><button @click="$store.commit('increment')">{{ $store.state.counter }}</nowiki><nowiki></button></nowiki>
 </template>