Vuex 状态管理
基本步骤
- 安装
Vuex
;
- 导入
Vuex
,使用插件 Vue.use(Vuex)
;
- 创建
store
;
- 挂载
Vue
实例;
安装
生产依赖
1
| $ npm install --save vuex
|
导入
1 2 3 4 5
| import Vue from 'vue' import Vuex from 'vuex'
Vue.use(Vuex)
|
创建 store
与别的插件不同,vuex
创建 store
实例是通过 new Vuex.Store()
。
1 2 3 4 5 6 7 8 9 10
| const store = new Vuex.Store({ state: {}, mutations: {}, actions: {}, getters: {}, modules: {} })
export default store
|
挂载 Vue
实例
1 2 3 4 5 6 7 8
| import Vue from 'vue' import store from './store'
new Vue({ el: '#app', store })
|
使用
State
组件内部可通过 this.$store.state.xxx
获取相应状态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <template> <div> <h1>{{ $store.state.title }}</h1> <p>{{ content }}</p> </div> </template>
<script> export default { data() { return { content: this.$store.state.content } } } </script>
|
Getters
Vuex
允许我们在 store
中定义“getter”(可以认为是 store
的计算属性)。就像计算属性一样,getter
的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
store
对象中定义 getters
方法; 每个 Getter
接受 state 作为其第一个参数:
1 2 3 4 5 6 7 8 9 10 11 12 13
| 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) } } })
|
- 组件内部通过
this.$store.getters
访问 :
1 2 3
| this.$store.getters.doneTodos
|
Getter
的第二参数
1 2 3 4 5 6 7 8 9
| getters: { doneTodos: (state) => { return state.todos.filter((todo) => todo.done) } doneTodosCount: (state, getters) => { return getters.doneTodos.length } }
|
1
| store.getters.doneTodosCount
|
Getter
利用函数传参
可以通过让 getter 返回一个函数,来实现给 getter 传参。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| getters: { getTodoById(state) { return function(id) { return state.todos.find(todo => todo.id === id) } } }
getters: { getTodoById: (state) => (id) => { return state.todos.find(todo => todo.id === id) } }
|
1
| this.$store.getters.getTodoById(2)
|
Mutations
(同步)
store
对象中定义 mutations
方法(mutation 必须是同步操作);
每个 mutation
都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数:
1 2 3 4 5 6 7 8 9 10 11 12 13
| const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment(state) { state.count++ }, decrement(state) { state.count-- } })
|
- 组件内部通过
this.$store.commit()
提交 mutations
;
1
| this.$store.commit('increment')
|
mutations
传参(Payload)
1 2 3 4 5 6
| mutations: { increment (state, n) { state.count += n } }
|
1
| this.$store.commit('increment', 10)
|
大多数情况下,载荷 payload
应该是一个对象:
1 2 3 4 5 6
| mutations: { increment (state, payload) { state.count += payload.amount } }
|
1 2 3
| this.$store.commit('increment', { amount: 10 })
|
对象方式提交 mutation
直接提交包含 type
属性的对象,type
指明方法名,且整个对象均作为payload
传递给 mutation
方法,即整个对象将作为 mutation
的第二参数。
1 2 3 4 5
| this.$store.commit({ type: 'increment', amount: 10, ... })
|
1 2 3 4 5
| mutations: { increment (state, payload) { state.count += payload.amount } }
|
Actions
(异步)
Action
类似于 mutation
,不同在于:
Action
函数接受一个与 store 实例具有相同方法和属性的 context
对象参数,因此可以调用 context.commit
提交一个 mutation,或者通过 context.state
和 context.getters
来获取 state 和 getters。
store
对象中定义 actions
方法(通过提交 mutation 来更新状态);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment(state) { state.count++ } }, actions: { increment(context) { setTimeout(() => { context.commit('increment') }, 1000) } } })
|
- 组件内部通过
this.$store.dispatch()
触发 action
;
1
| this.$store.dispatch('increment')
|
Actions
传参及对象方式分发
Actions 支持同 Mutations 一样可通过载荷方式传参和对象方式进行分发:
1 2 3 4 5 6 7 8 9 10
| store.dispatch('incrementAsync', { amount: 10 })
store.dispatch({ type: 'incrementAsync', amount: 10 })
|
模块化 Module
Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| const moduleA = { state: { ... }, mutations: { ... }, actions: { ... }, getters: { ... } }
const moduleB = { state: { ... }, mutations: { ... }, actions: { ... } }
const store = new Vuex.Store({ modules: { a: moduleA, b: moduleB } })
|
局部状态
对于模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态对象。对于模块内部的 action,局部状态通过 context.state
暴露出来:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| const moduleA = { state: { count: 0 }, mutations: { increment(state) { state.count++ } }, getters: { doubleCount(state) { return state.count * 2 } }, actions: { incrementIfOddOnRootSum(context) { if (context.state % 2 === 1) { context.commit('increment') } } } }
|
添加和删除状态
在 Vuex
中,只有在 state
定义的状态才会被实时监听,直接在 mutations
中为某一状态添加对象或属性,是不会被响应式处理的。如:
1 2 3 4 5 6 7 8 9 10 11 12 13
| state: { info: { name: 'Vincent', age: 18 } }, mutations: { updateInfo(state) { state.info['location'] = 'Guangzhou' delete state.info.age } }
|
正确地添加和删除状态是使用 Vue.set()
和 Vue.delete()
:
1 2 3 4 5 6
| mutations: { updateInfo(state) { Vue.set(state.info, 'location', 'Guangzhou') Vue.delete(state.info, 'age') } }
|
组件辅助函数
mapState
在组件中可以使用 mapState
辅助函数将状态映射成计算属性,减少计算属性声明带来的冗余。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import { mapState } from 'vuex'
export default { computed: { localComputed() { }, ...mapState({
count: state => state.count,
countAlias: 'count',
countPlusLocalState (state) { return state.count + this.localCount } }, ...mapState([ 'count' ]) }) }
|
mapGetters
同上,映射 getters
=> computed
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import { mapGetters } from 'vuex'
export default { computed: { ...mapGetters([ 'doneTodosCount', 'anotherGetter' ]), ...mapGetters({ doneCount: 'doneTodosCount' }) } }
|
mapMutations
同上,映射 mutations
=> methods
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import { mapMutations } from 'vuex'
export default { methods: { ...mapMutations([ 'increment',
'incrementBy' ]), ...mapMutations({ add: 'increment' }) } }
|
mapActions
同上,映射 actions
=> methods
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import { mapActions } from 'vuex'
export default { methods: { ...mapActions([ 'increment',
'incrementBy' ]), ...mapActions({ add: 'increment' }) } }
|
参考文献: