Vuex学习

111 阅读4分钟

作用:

统一管理项目状态的,也是一种比较好的组件传递信息的方式;

关键内容:

  • state:

    状态数据的储存位置(类似data);
    
  • getters:

    可对state中data返回进行加工,并缓存,只有当依赖的data值发生变化时才重新计算,返回计算后的值;
    类似计算属性,例如需要将data进行拼接,多个组件都需要使用时;
    
  • mutations:

    提交修改state中数据内容的唯一方式,只能是同步代码;
    组件中使用方式:this.$store.commit('事件类型');
    
  • actions:

    异步代码,获取到异步数据后,再通过commit提交mutations中的方法,修改更新state数据;
    组件中的使用方式:this.$store.dispatch('事件类型');
    
  • Module:

    将store分割成模块(module);
    每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块:
    

实例详情说明:

1.state:

1.1 辅助函数mapState:当需要使用多个state时,使用辅助函数生成多个state的计算属性;
//store.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex)
export default new Vuex.Store({
    state:{
        count:4
    }
})

// 组件使用
// 对象形式
computed:mapState({
    count: state => state.count, // 返回count,等同于count() {return this.$store.state.count}
    countAlias: 'count', // count的别名是countAlias,这时候countAlias等同于count(注意单引号)

    sumCount(state) { // 处理多状态:store的数据加上本地的数据
        return state.count+this.localCount
    }
})
// 数组形式
computed: mapState([
  'count'  //等同于count:state=>state.count,
])

2.getters:

2.1应用:
const store = new Vuex.Store({
  state: {
    todos: [
      { id: 1, text: '...', done: true },
      { id: 2, text: '...', done: false }
    ]
  },
    
  getters: {
    doneTodos: state => {
        return state.todos.filter(todo => todo.done)
    },
    doneTodosCount: (state, getters) => { // 接受第二个参数getter
        return getters.doneTodos.length //通过getter访问到doneTodos属性
    },
    getTodoById: (state) => (id) => { // 让 getter 返回一个函数,来实现给 getter 传参。
        return state.todos.find(todo => todo.id === id)
    },
  }
})
2.2访问:
//访问
store.getters.doneTodos
store.getters.doneTodosCount
store.getters.getTodoById(2) // 返回元素id与传过去的id相等的元素

//组件中使用
computed: {
  doneTodosCount () {
    return this.$store.getters.doneTodosCount
  }
}
2.3辅助函数mapGetters:
import { mapGetters } from 'vuex'

export default {
  // ...
  computed: {
    // 使用对象展开运算符,将 getter 混入 computed 对象中
    ...mapGetters([
      'doneTodosCount',
      'doneTodos',
      // ...
    ])
  }
}

3. Mutation:

3.1 应用:
const store = new Vuex.Store({
  state: {
    count: 1
  },
    
  mutations: {
    //事件类型:increment;回调函数为后面部分;
    incrementA (state) {
        state.count ++;
    },
    incrementB (state, n) { // 额外参数载荷(payload)
        state.count += n;
    },
    incrementC (state, payload) { // 大多数情况下,载荷应该是一个对象
        state.count += payload.amount
    }
  }
})
3.2 提交:
// 提交
store.commit('incrementA')
store.commit('incrementB', 6) // 提交载荷
store.commit('incrementC', { amount: 7 })

// 组件中提交
this.$store.commit('事件类型', payload)
3.3 辅助函数 mapMutations:
// 展开store中的mutations方法,以便组件中直接this调用
methods:{
    //...
    ...mapMutations([
        'incrementA',  //相当于this.$store.commit('increment')
        'incrementB', //相当于`this.$store.commit('incrementB', n)`
        'incrementC' //相当于this.$store.commit('incrementC', { amount } )
    ]),
}

4. action:

4.1 应用:
//一个往数组添加随机数的实例:
const store = new Vuex.Store({
  state: {
    msg: []
  },
  mutations: {
    addMessage(state,msg){
      state.msg.push(msg)
    }
  },
  
//一个异步操作:
actions: {
     getMessages({ context }) { // 可使用 { commit } 直接解析commit出来
       // 异步调用接口后,再使用commit提交mutations中的方法修改state数据;
       fetch('https://url/api/getnum')
         .then(res => res.json())
         .then(data => {
           context.commit('addMessage',data.data.number)
         })       
     },
     async actionA ({ commit }) {
        // 获取到接口getDataInfo后,提交到mutations中的gotData中;
        commit('gotData', await getDataInfo())
      },
      async actionB ({ dispatch, commit }) { // 组合
        await dispatch('actionA') // 等待 actionA 完成
        commit('gotOtherData', await getOtherDataInfo())
    }

 }
})
4.2 分发:
// 分发
store.dispatch('getMessages');
store.dispatch('事件类型', { amount: 1 }) // 载荷分发
store.dispatch({ type: '事件类型', amount: 1 }) // 对象形式 载荷分发

// 组件中分发
this.$store.dispatch('事件类型')
4.3 辅助函数mapActions:
import { mapActions } from 'vuex'

export default {
  // ...
  methods: {
    ...mapActions([
      'getMessages', // 相当于`this.$store.dispatch('getMessages')`
    ]),
    ...mapActions({
      addmsg: 'getMessages' //this.addmsg等同于`this.$store.dispatch('getMessages')`
    })
  }
}

5. module:

5.1 应用:
const moduleA = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态

命名空间:

默认情况,模块内部 action、mutation 和 getter 注册在全局命名空间,使得多个模块能对同一 mutation 或 action 作出响应。
模块对象中加入 namespaced: true 使其成为带命名空间的模块,所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名,具有更高的封装度和复用性;
const store = new Vuex.Store({
  modules: {
    account: {
      namespaced: true,
      state: () => ({ ... }), 
      getters: {isAdmin () { ... }}, // 通过 store.getters['account/isAdmin']访问
      actions: {login () { ... }}, // 通过 store.dispatch('account/login')分发
      mutations: {login () { ... }}, //通过store.commit('account/login')提交

      // 模块中嵌套模块 - 继承父模块的命名空间
      modules: {
        // 模块1 - myPage
        myPage: {
          state: () => ({ ... }),
          getters: {profile () { ... }} // store.getters['account/profile']
        },

        // 模块2 - mposts - 进一步嵌套命名空间
        posts: {
          namespaced: true,
          state: () => ({ ... }),
          getters: {popular () { ... }} // store.getters['account/posts/popular']
        }
      }
    }
  }
})
在带命名空间的模块内访问全局内容:

rootStaterootGetters 会作为第三和第四参数传入 getter,也会通过 context 对象的属性传入 action。

若需要在全局命名空间内分发 action 或提交 mutation,将 { root: true } 作为第三参数传给 dispatchcommit 即可。

modules: {
  foo: {
    namespaced: true, // 命名空间设置
    getters: {
      someOtherGetter: state => { ... }
      someGetter (state, getters, rootState, rootGetters) { // 1. 对于getter,使用第四个参数 `rootGetters`访问根getter
        getters.someOtherGetter // -> 局部的
        rootGetters.someOtherGetter // -> 全局的
      },
    },

    actions: {
      someOtherAction (ctx, payload) { ... }
      someAction ({ dispatch, commit, getters, rootGetters }) { // 2. 对于actions,接受 root 属性来访问根 dispatch 或 commit
        dispatch('someOtherAction') // -> 局部的分发
        dispatch('someOtherAction', null, { root: true }) // -> 全局的分发

        commit('someMutation') // -> 局部的提交
        commit('someMutation', null, { root: true }) // -> 全局的提交
      },
    }
  }
}
在带命名空间的模块注册全局 action:

在action中添加 root: true,并将这个 action 的定义放在函数 handler 中。例如:

{
  //...
  modules: {
    foo: {
      namespaced: true,
      actions: {
        someAction: {
          root: true,
          handler (namespacedContext, payload) { ... } // -> 定义someAction action
        }
      }
    }
  }
}
带命名空间的绑定函数:

当使用 mapStatemapGettersmapActions 和 mapMutations 这些函数来绑定带命名空间的模块时:

普通:

computed: {
  ...mapState({
    a: state => state.some.nested.module.a,
    b: state => state.some.nested.module.b
  })
},
methods: {
  ...mapActions([
    'some/nested/module/foo', // -> this['some/nested/module/foo']()
    'some/nested/module/bar' // -> this['some/nested/module/bar']()
  ])
}

优化一:

computed: {
  ...mapState('some/nested/module', {
    a: state => state.a,
    b: state => state.b
  })
},
methods: {
  ...mapActions('some/nested/module', [
    'foo', // -> this.foo()
    'bar' // -> this.bar()
  ])
}

优化二:

import { createNamespacedHelpers } from 'vuex'
const { mapState, mapActions } = createNamespacedHelpers('some/nested/module') // 基于某个命名

export default {
  computed: {
    ...mapState({
      a: state => state.a,
      b: state => state.b
    })
  },
  methods: {
    ...mapActions([
      'foo',
      'bar'
    ])
  }
}