• Vuex 状态树
    • 使用状态树
      • 普通方式
      • 模块文件
      • 插件
    • fetch 方法
    • nuxtServerInit 方法
    • Vuex 严格模式
      • Module Mode
      • 经典模式

    Vuex 状态树

    对于每个大项目来说,使用状态树 (store) 管理状态 (state) 十分有必要。这就是为什么 Nuxt.js 内核实现了 Vuex。

    使用状态树

    Nuxt.js 会尝试找到应用根目录下的 store 目录,如果该目录存在,它将做以下的事情:

    • 引用 vuex 模块
    • vuex 模块 加到 vendors 构建配置中去
    • 设置 Vue 根实例的 store 配置项 Nuxt.js 支持两种使用 store 的方式,你可以择一使用:

    • 模块方式: store 目录下的每个 .js 文件会被转换成为状态树指定命名的子模块 (当然,index 是根模块)

    • Classic(不建议使用): store/index.js返回创建Vuex.Store实例的方法。

    无论使用那种模式,您的state的值应该始终是function,为了避免返回引用类型,会导致多个实例相互影响。

    普通方式

    Nuxt.js允许您拥有一个 store 目录,其中包含与模块对应的每个文件。

    首先,只需将状态导出为 函数,将变量和操作作为 store/index.js 中的对象导出:

    1. export const state = () => ({
    2. counter: 0
    3. })
    4. export const mutations = {
    5. increment (state) {
    6. state.counter++
    7. }
    8. }

    然后,您可以拥有 store/todos.js 文件:

    1. export const state = () => ({
    2. list: []
    3. })
    4. export const mutations = {
    5. add (state, text) {
    6. state.list.push({
    7. text: text,
    8. done: false
    9. })
    10. },
    11. remove (state, { todo }) {
    12. state.list.splice(state.list.indexOf(todo), 1)
    13. },
    14. toggle (state, todo) {
    15. todo.done = !todo.done
    16. }
    17. }

    Vuex将如下创建:

    1. new Vuex.Store({
    2. state: () => ({
    3. counter: 0
    4. }),
    5. mutations: {
    6. increment (state) {
    7. state.counter++
    8. }
    9. },
    10. modules: {
    11. todos: {
    12. namespaced: true,
    13. state: () => ({
    14. list: []
    15. }),
    16. mutations: {
    17. add (state, { text }) {
    18. state.list.push({
    19. text,
    20. done: false
    21. })
    22. },
    23. remove (state, { todo }) {
    24. state.list.splice(state.list.indexOf(todo), 1)
    25. },
    26. toggle (state, { todo }) {
    27. todo.done = !todo.done
    28. }
    29. }
    30. }
    31. }
    32. })

    在您的 pages/todos.vue 中,使用 todos 模块:

    1. <template>
    2. <ul>
    3. <li v-for="todo in todos">
    4. <input type="checkbox" :checked="todo.done" @change="toggle(todo)">
    5. <span :class="{ done: todo.done }">{{ todo.text }}</span>
    6. </li>
    7. <li><input placeholder="What needs to be done?" @keyup.enter="addTodo"></li>
    8. </ul>
    9. </template>
    10. <script>
    11. import { mapMutations } from 'vuex'
    12. export default {
    13. computed: {
    14. todos () {
    15. return this.$store.state.todos.list
    16. }
    17. },
    18. methods: {
    19. addTodo (e) {
    20. this.$store.commit('todos/add', e.target.value)
    21. e.target.value = ''
    22. },
    23. ...mapMutations({
    24. toggle: 'todos/toggle'
    25. })
    26. }
    27. }
    28. </script>
    29. <style>
    30. .done {
    31. text-decoration: line-through;
    32. }
    33. </style>

    模块方法也适用于顶级定义,而无需在 store 目录中实现子目录

    示例:您创建文件 store/state.js 并添加以下内容

    1. export default () => ({
    2. counter: 0
    3. })

    相应的可以在文件夹中添加 store/mutations.js

    1. export default {
    2. increment (state) {
    3. state.counter++
    4. }
    5. }

    模块文件

    您可以将模块文件分解为单独的文件:state.js,actions.js,mutations.jsgetters.js。如果您使用index.js来维护state,getters,actionsmutations,同时具有单个单独的操作文件,那么仍然可以正确识别该文件。

    注意:在使用拆分文件模块时,必须记住使用箭头函数功能this 在词法上可用。词法范围this意味着它总是指向引用箭头函数的所有者。如果未包含箭头函数,那么this将是未定义的(undefined)。解决方案是使用 "normal" 功能,该功能会将this指向自己的作用域,因此可以使用。

    插件

    您可以将其他插件添加到store(在模块模式下),将其放入store/index.js文件中:

    1. import myPlugin from 'myPlugin'
    2. export const plugins = [ myPlugin ]
    3. export const state = () => ({
    4. counter: 0
    5. })
    6. export const mutations = {
    7. increment (state) {
    8. state.counter++
    9. }
    10. }

    有关插件的更多信息: Vuex 文档.

    fetch 方法

    fetch 方法会在渲染页面前被调用,作用是填充状态树 (store) 数据,与 asyncData 方法类似,不同的是它不会设置组件的数据。

    关于 fetch 方法的更多信息,请参考 页面 fetch 方法API。

    nuxtServerInit 方法

    如果在状态树中指定了 nuxtServerInit 方法,Nuxt.js 调用它的时候会将页面的上下文对象作为第2个参数传给它(服务端调用时才会酱紫哟)。当我们想将服务端的一些数据传到客户端时,这个方法是灰常好用的。

    举个例子,假设我们服务端的会话状态树里可以通过 req.session.user 来访问当前登录的用户。将该登录用户信息传给客户端的状态树,我们只需更新 store/index.js 如下:

    1. actions: {
    2. nuxtServerInit ({ commit }, { req }) {
    3. if (req.session.user) {
    4. commit('user', req.session.user)
    5. }
    6. }
    7. }

    如果你使用状态树模块化的模式,只有主模块(即 store/index.js)适用设置该方法(其他模块设置了也不会被调用)。

    这时context被赋予nuxtServerInit作为第二个参数,它与asyncDatafetch方法相同。

    nuxtServerInit 方法接收的上下文对象和 fetch 的一样,但不包括 context.redirect()context.error()

    注意:异步nuxtServerInit操作必须返回Promise来通知nuxt服务器等待它们。

    1. actions: {
    2. async nuxtServerInit({ dispatch }) {
    3. await dispatch('core/load')
    4. }
    5. }

    Vuex 严格模式

    默认情况下,在开发模式下启用严格模式,在生产模式下关闭模式。要在dev中禁用严格模式,请遵循以下示例。

    Module Mode

    export const strict = false

    经典模式

    此功能已经弃用,将在Nuxt 3中删除。

    要使用经典模式创建Vuex,我们应该创建store/index.js到处返回Vuex实例的方法的文件:

    1. import Vuex from 'vuex'
    2. const createStore = () => {
    3. return new Vuex.Store({
    4. strict: false,
    5. state: () => ({
    6. counter: 0
    7. }),
    8. mutations: {
    9. increment (state) {
    10. state.counter++
    11. }
    12. }
    13. })
    14. }
    15. export default createStore

    我们不需要安装,因为Vuex由Nuxt.js提供。

    我们现在可以在我们的组件中使用this.$store

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