Vuex重学
State
用计算属性来获得store中的值
computed: {
count () {
return this.$store.state.count
}
}
每当 store.state.count
变化的时候, 都会重新求取计算属性,并且触发更新相关联的 DOM
mapState 辅助函数
可以帮助生成计算属性
// 完整写法
computed: {
// ...是对象展开运算符
...mapState({
count: state => state.count,
message: state => state.message
})
}
// 简化写法
computed: mapState({
count: state => state.count,
message: state => state.message
})
表示将 state.count
和 state.message
绑定到计算属性 count
和 message
上,使得 Vue 组件中可以直接访问这些状态
computed: {
...mapState({
// 传字符串参数 'count' 等同于 `state => state.count`
countAlias: 'count',
// 当映射的计算属性的名称与 state 的子节点名称相同时,我们也可以给 mapState 传一个字符串数组
// 映射 this.count 为 store.state.count
'count'
})
}
// 也可以通过传递数组方式来进行映射
computed: {
...mapState(['count', 'message'])
}
Getter
可以认为是 store 的计算属性
Getter 接受 state 作为其第一个参数
const store = createStore({
state: {
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
]
},
getters: {
doneTodos (state) {
return state.todos.filter(todo => todo.done)
}
}
})
也可以接受其他 getter 作为第二个参数
getters: {
// ...
doneTodosCount (state, getters) {
return getters.doneTodos.length
}
}
注:可以通过store.getters
对象来访问getter
同理可以使用mapGetters
辅助函数将 store 中的 getter 映射到局部计算属性
Mutation
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation
const store = createStore({
state: {
count: 1
},
mutations: {
// 接收state作为第一个参数
increment (state) {
// 变更状态
state.count++
}
}
})
注:用store.commit('increment')
来调用mutations中的方法
- 可以向
store.commit
传入额外的参数,即 mutation 的载荷(payload)
mutations: {
increment (state, n) {
state.count += n
}
}
store.commit('increment', 10)
- 提交 mutation 的另一种方式是直接使用包含
type
属性的对象:
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
store.commit({
type: 'increment',
amount: 10
})
注:mutations中是同步函数
同理可以使用 mapMutations
辅助函数将组件中的 methods 映射为 store.commit
调用
import { mapMutations } from 'vuex'
export default {
// ...
methods: {
...mapMutations([
'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`
// `mapMutations` 也支持载荷:
'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)`
]),
...mapMutations({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
})
}
}
Action
处理异步操作
Action 提交的是 mutation,而不是直接变更状态,也就是说,需要更改store中的数据的时候需要去调用mutation
const store = createStore({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
// 简化
actions: {
increment ({ commit }) {
commit('increment')
}
}
Action 通过 store.dispatch
方法触发
store.dispatch('increment')
Actions 支持同样的载荷方式和对象方式进行分发
// 以载荷形式分发
store.dispatch('incrementAsync', {
amount: 10
})
// 以对象形式分发
store.dispatch({
type: 'incrementAsync',
amount: 10
})
可以使用mapActions
辅助函数
模块化
Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter,也可以进行嵌套,拥有自己的子模块。
模块的局部状态
对于模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态对象
const moduleA = {
state: () => ({
count: 0
}),
mutations: {
increment (state) {
// 这里的 `state` 对象是模块的局部状态
state.count++
}
},
getters: {
doubleCount (state) {
return state.count * 2
}
}
}
模块内部的 action,局部状态通过 context.state
暴露出来,根节点状态则为 context.rootState
对于模块内部的 getter,根节点状态会作为第三个参数rootState
暴露出来
命名空间
可以通过添加 namespaced: true
的方式使其成为带命名空间的模块(防止命名冲突)
const store = new Vuex.Store({
modules: {
foo: {
namespaced: true,
actions: {
callBarMutation({ commit }) {
commit('bar/mutationName')
}
}
},
bar: {
namespaced: true,
mutations: {
mutationName(state) { ... }
}
}
}
})
如果你希望使用全局 state 和 getter,rootState
和 rootGetters
会作为第三和第四参数传入 getter,也会通过 context
对象的属性传入 action。
若需要在全局命名空间内分发 action 或提交 mutation,将 { root: true }
作为第三参数传给 dispatch
或 commit
即可。
modules: {
foo: {
namespaced: true,
getters: {
// 在这个模块的 getter 中,`getters` 被局部化了
// 你可以使用 getter 的第四个参数来调用 `rootGetters`
someGetter (state, getters, rootState, rootGetters) {
getters.someOtherGetter // -> 'foo/someOtherGetter'
rootGetters.someOtherGetter // -> 'someOtherGetter'
rootGetters['bar/someOtherGetter'] // -> 'bar/someOtherGetter'
},
someOtherGetter: state => { ... }
},
actions: {
// 在这个模块中, dispatch 和 commit 也被局部化了
// 他们可以接受 `root` 属性以访问根 dispatch 或 commit
someAction ({ dispatch, commit, getters, rootGetters }) {
getters.someGetter // -> 'foo/someGetter'
rootGetters.someGetter // -> 'someGetter'
rootGetters['bar/someGetter'] // -> 'bar/someGetter'
dispatch('someOtherAction') // -> 'foo/someOtherAction'
dispatch('someOtherAction', null, { root: true }) // -> 'someOtherAction'
commit('someMutation') // -> 'foo/someMutation'
commit('someMutation', null, { root: true }) // -> 'someMutation'
},
someOtherAction (ctx, payload) { ... }
}
}
}
在带命名空间的模块注册全局 action,你可添加 root: true
,并将这个 action 的定义放在函数 handler
中
{
actions: {
someOtherAction ({dispatch}) {
dispatch('someAction')
}
},
modules: {
foo: {
namespaced: true,
actions: {
someAction: {
root: true,
handler (namespacedContext, payload) { ... } // -> 'someAction'
}
}
}
}
}
优化映射函数
将模块的空间名称字符串作为第一个参数传递给上述函数,这样所有绑定都会自动将该模块作为上下文。
computed: {
...mapState('some/nested/module', {
a: state => state.a,
b: state => state.b
}),
...mapGetters('some/nested/module', [
'someGetter', // -> this.someGetter
'someOtherGetter', // -> this.someOtherGetter
])
},
methods: {
...mapActions('some/nested/module', [
'foo', // -> this.foo()
'bar' // -> this.bar()
])
}