Vuex 使用说明
官方文档地址:https://vuex.vuejs.org/zh/
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store。
- Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
- 你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
1 vuex的声明
vuex的声明文件一般存放在项目根目录中有一个store文件夹:
1 2 3 4 5 6 7 8
| ./ ├── actions.js ├── index.js ├── modules │ ├── customInfo.js │ └── global.js ├── modules.js └── mutation.js
|
一个vuex的文件:
1 2
| export const SOME_MUTATION = 'SOME_MUTATION'
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| import { SOME_MUTATION } from './mutation-types'
export default { state: { todos: [ { id: 1, text: '...', done: true } ] },
getters: { doneTodos: state => { return state.todos.filter(todo => todo.done) }, doneTodosCount: (state, getters) => { return getters.doneTodos.length }, getTodoById: (state) => (id) => { return state.todos.find(todo => todo.id === id) } },
mutations: { increment (state) { state.count++ }, increment (state, payload) { state.count += payload.amount },
[SOME_MUTATION] (state) { } },
actions: { increment (context) { context.commit('increment') }, increment ({ commit }, payload) { commit('increment') }, actionA ({ commit }) { return new Promise((resolve, reject) => { setTimeout(() => { commit('someMutation') resolve() }, 1000) }) }, actionB ({ dispatch, commit }) { return dispatch('actionA').then(() => { commit('someOtherMutation') }) }, async actionC ({ commit }) { commit('gotData', await getData()) }, async actionD ({ dispatch, commit }) { await dispatch('actionA') commit('gotOtherData', await getOtherData()) } }, }
|
2 vuex的使用
2.1 vuex文件挂载
一般在项目的src文件夹下有一个store.js文件:
1 2 3 4 5 6 7 8 9 10 11 12
| import Vue from 'vue' import Vuex from 'vuex'
import actions from './store/actions' import modules from './store/modules' Vue.use(Vuex)
const store = new Vuex.Store({ actions, modules, }) export default store;
|
然后src文件夹下的main.js文件中使用:
1 2 3 4 5 6 7 8 9
| import App from './App.vue' import router from './router.js' import store from './store.js'
new Vue({ render: h => h(App), router, store }).$mount('#app')
|
2.2 在组件中使用vuex
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
| import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'
export default { computed: { count () { return this.$store.state.count }, ...mapState({ count: state => state.count, countAlias: 'count', countPlusLocalState (state) { return state.count + this.localCount } }) ...mapState([ 'count' ]), doneTodos () {this.$store.getters.doneTodos}, ...mapGetters([ 'doneTodosCount', 'anotherGetter', ]), ...mapGetters({ doneCount: 'doneTodosCount' }) }, methods: { getIncrement() { this.$store.commit('increment') this.$store.commit('increment', 10) this.$store.commit({ type: 'increment', amount: 10 }) }, ...mapMutations([ 'increment', 'incrementBy' ]), ...mapMutations({ setTemplateSeq: 'SET_TEMPLATESEQ', add: 'increment' }), addIncrement() { this.$store.dispatch('increment') this.$store.dispatch('incrementAsync', { amount: 10 }) this.$store.dispatch({ type: 'incrementAsync', amount: 10 }) this.$store.dispatch('actionA').then(() => { }) }, ...mapActions([ 'increment', 'incrementBy' ]), ...mapActions({ add: 'increment' }) } }
|
3. Module模块
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割。
默认情况下,模块内部的 action 和 mutation 仍然是注册在全局命名空间的——这样使得多个模块能够对同一个 action 或 mutation 作出响应。Getter 同样也默认注册在全局命名空间,但是目前这并非出于功能上的目的(仅仅是维持现状来避免非兼容性变更)。必须注意,不要在不同的、无命名空间的模块中定义两个相同的 getter 从而导致错误。
如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| const moduleA = {
state: () => ({ count: 0 }), getters: { sumWithRootCount (state, getters, rootState) { return state.count + rootState.count } }, mutations: { increment (state) { state.count++; } }, actions: { incrementIfOddOnRootSum ({ state, commit, rootState }) { if ((state.count + rootState.count) % 2 === 1) { commit('increment') } } }, }
const moduleB = { state: () => ({ ... }), } const store = new Vuex.Store({ modules: { a: moduleA, b: moduleB } })
store.state.a store.state.b
|
3.1 命名空间
如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。
启用了命名空间的 getter 和 action 会收到局部化的 getter,dispatch 和 commit。
如果你希望使用全局 state 和 getter,rootState 和 rootGetters 会作为第三和第四参数传入 getter,也会通过 context 对象的属性传入 action。
若需要在全局命名空间内分发 action 或提交 mutation,将 { root: true } 作为第三参数传给 dispatch 或 commit 即可。
若需要在带命名空间的模块注册全局 action,你可添加 root: true,并将这个 action 的定义放在函数 handler 中。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| modules: { foo: { namespaced: true, getters: { someGetter (state, getters, rootState, rootGetters) { getters.someOtherGetter rootGetters.someOtherGetter }, someOtherGetter: state => { ... } },
actions: { someAction ({ dispatch, commit, getters, rootGetters }) { getters.someGetter rootGetters.someGetter
dispatch('someOtherAction') dispatch('someOtherAction', null, { root: true })
commit('someMutation') commit('someMutation', null, { root: true }) }, someOtherAction (ctx, payload) { ... }, someAction: { root: true, handler (namespacedContext, payload) { ... } } } } }
|
3.2 带命名空间模块的使用
第一种直接使用:
1 2 3 4 5 6 7 8 9 10 11 12
| computed: { ...mapState({ a: state => state.some.nested.module.a, b: state => state.some.nested.module.b }) }, methods: { ...mapActions([ 'some/nested/module/foo', 'some/nested/module/bar' ]) }
|
第二种,将模块的空间名称字符串作为第一个参数传递给上述函数,这样所有绑定都会自动将该模块作为上下文:
1 2 3 4 5 6 7 8 9 10 11 12
| computed: { ...mapState('some/nested/module', { a: state => state.a, b: state => state.b }) }, methods: { ...mapActions('some/nested/module', [ 'foo', 'bar' ]) }
|
第三种,通过vuex的createNamespacedHelpers 创建基于某个命名空间辅助函数。它返回一个对象,对象里有新的绑定在给定命名空间值上的组件绑定辅助函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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' ]) } }
|