学前准备
本文主要内容来源于官网,为Vuex的基础部分总结,部分案例也参考了互联网上其他分享知识的大佬。本手记结合官网内容也额外添加了自己的一些的理解,希望能给你带来一些参考价值,如果文章有理解不到位的地方,还请各位多批评指正!以下是本文中可能用到的参考资料:
点击进入vuex官方教程
点击进入Vue CLI官方教程
点击了解Vuex Action中的参数解构为什么那么写
点击了解Action和Promise中Promise详解
为什么使用Vuex
Vuex的官方解答:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
在vue开发的过程中,我们经常遇到一个状态可能会在多个组件之间使用,比如用户信息、头像、昵称等,我们修改了这些信息,那么整个应用中凡是使用了该信息的部分都应该更新。想让vue
中所有组件都共享这些信息,那么就需要将这些信息进行集中管理,这个时候我们就需要用到Vuex
。
通过Vue CLI生成项目模板
日常开发中,我们大多都是用Vue CLI
脚手架来生成一个vue项目,不太清楚脚手架怎么使用的可以移步Vue CLI官网自行查阅。在使用脚手架生成的项目时会让我们选择store
,选择后会在页面中给我们生成store
文件夹,自带初始化仓库的index.js
,这就是最初的store
,里面结构如下:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {},
mutations: {},
actions: {},
modules: {}
})
在模板中我们可以看到state
、mutations
、actions
、modules
这几个对象,而Vuex
中几乎所有的操作都是围绕它们展开的,接下来我们就来逐一认识它们。
State
Vuex
的核心仓库是store
,这个store
实例会被注入到所有的子组件里面,而所有子组件中公用的数据信息和状态就都存储在State
里面。我们来看一个简单的小栗子。
export default new Vuex.Store({
state: {
count: 102
}
})
vuex
中所有的状态管理都会定义在 state
中,我们在 state
中定义了 count
,那么我们就得到了一个集中管理的状态 count
,所有的组件都可以直接访问到这个 count
。
{{count}}
因为根实例中注册 store
选项,该 store
实例会注入到根组件下的所有子组件中,且子组件能通过 this.$store
访问到。通过计算属性,我们就可以在模板里面使用模板语法来调用count
了,当然我们也可以在模板中的直接写入。有小伙伴会好奇为什么不能写在data
里或其它地方吗,state
写在计算属性中是因为模板文件需要响应仓库中state
的变化,而state
变化之后如果写在data
中就不能及时响应渲染到页面中,而computed
身为计算属性会监听函数下面值得变化然后进行重新渲染。
{{this.$store.state.count}}
-
mapState
如果我们在store
中存储了很多个状态,而在当前模板页面中又要读取store
里面大量状态,那么我们在当前页面采用computed
中来return store
里面的状态这种写法就会变得很冗余。Vuex
中针对state
给我们提供了它的语法糖mapState
,我们可以通过mapState()
方法直接获取state中存储的状态,如下栗子:
{{count}}
{{total}}
// 102
// 112
Getter
有时候我们需要从 store
中的 state
中派生出一些状态,例如如果state
中有一个数组,我们要将数组中所有大于10的数字挑选出来然后在给组件使用,栗子如下:
{{modiyArr}}
// [20, 40, 50, 66, 77, 88]
我们在computed
中完成了代码逻辑,同时也暴露出了一些问题,就是如果我们需要在多个组件中都完成这个数组的过滤,就需要在各个组件处都复制这段代码或者将其抽取到一个共享函数然后在多处导入它,无论哪一种方式都不是很理想。这个时候我们就可以使用vuex
中的getter
。
Vuex
允许我们在 store
中定义“getter
”(可以认为是 store
的计算属性)。就像计算属性一样,getter
的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
{{this.$store.getters.modiyArr}}
{{modiyArr}}
{{getLength}}
// stoer.js
export default new Vuex.Store({
state: {
list: [1, 3, 4, 10, 20, 40, 50, 66, 77, 88]
},
getters: {
// getters中的函数接收两个参数(state, getters)
modiyArr(state) {
return state.list.filter(item => item > 10)
},
getLength(state, getters) {
return getters.modiyArr.length // 可以通过getters直接调用它的其它方法
}
}
})
// [20, 40, 50, 66, 77, 88]
上面的栗子基本都是通过属性来访问的,使用getters
也可以支持通过方法来访问,我们来看一个官网的小栗子
// 筛选state中的额todos,将todos数组中id为2的对象找出来
// store.js
const store = new Vuex.Store({
state: {
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
]
},
getters: {
doneTodos: state => (id) => {
return state.todos.find(todo => todo.id === id)
}
}
})
{{checkDoneTodos}}
// { id: 2, text: '...', done: false }
注意,
getter
在通过属性访问时是作为Vue
的响应式系统的一部分缓存其中的。getter
在通过方法访问时,每次都会去进行调用,而不会缓存结果。
-
mapGetters
mapGetters
也是Vuex
帮我们封装好的语法糖,具体用法其实和mapState
差不多。
import { mapGetters } from 'vuex'
export default {
// ...
computed: {
// 使用对象展开运算符将 getter 混入 computed 对象中
...mapGetters([
'modiyArr',
'getLength',
// ...
])
}
}
当然...mapGetters
本身也是支持对象类写法的
...mapGetters({
// 把 `this.editArr` 映射为 `this.$store.getters.modiyArr`
editArr: 'modiyArr',
getLength: 'getLength'
})
Mutation
如果我们需要对store.js
中的状态进行修改,我们是不能在组件里面直接修改的,因为组件里面直接修改仓库是不会进行全局响应的,这就违背了我们使用仓库的初衷。唯一的方法就是通过提交mutation
来进行修改,这样仓库中与之对应的状态也会被修改进而影响全部组件的数据。Vuex
中的 mutation
非常类似于事件:每个 mutation
都有一个字符串的 事件类型 (type)
和 一个 回调函数 (handler)
。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state
作为第一个参数:
{{num}}
export default new Vuex.Store({
state: {
num: 10
},
mutations: {
add (state) {
// 变更状态
state.num++
},
reduce (state) {
state.num--
}
}
})
上面栗子中,我们在state
中定义了num
,然后通过mutations
添加add
方法和reduce
方法去对num
进行修改,在模板文件中我们定义了click
事件来改变num
的值,模板文件中的事件去响应store
中的mutations
主要是通过commit()
来实现的。
-
提交载荷(Payload)
你可以向store.commit
传入额外的参数,即 mutation 的 载荷(payload)
,其实换种方式理解可能更容易,就是模板文件中的commit
可以添加额外的参数,mutations
中接收的时候接收一个形参payload
,就代表模板文件中你传进来的参数。vuex
官网更建议我们payload
应该是一个对象,这样可以包含多个字段并且记录的 mutation
会更易读,我们看下面的小栗子:
export default {
methods: {
add() {
//this.$store.commit('add', 100)直接传递额外参数,不建议,更建议下一种对象的写法
this.$store.commit('add', {
addNum: 100
})
}
}
}
export default new Vuex.Store({
state: {
num: 10
},
mutations: {
add (state, payload) {
state.num+=payload.addNum
},
reduce (state) {
state.num--
}
}
})
也可以将所有参数写到一个对象里面,那么type
就对应mutations
中要执行的函数名
export default {
methods: {
add() {
this.$store.commit({
type: 'add',
addNum: 100
})
}
}
}
-
Mutation 需遵守 Vue 的响应规则
这个通俗点说就是你在开发过程中需要向state
里面添加额外数据时,需要遵循响应准则。官方文档说既然 Vuex
的 store
中的状态是响应式的,那么当我们变更状态时,监视状态的 Vue
组件也会自动更新。这也意味着 Vuex
中的 mutation
也需要与使用Vue
一样遵守一些注意事项: 1.最好提前在你的 store
中初始化好所有所需属性。 2.当需要在对象上添加新属性时,你应该使用Vue.set(obj, 'newProp', 123)
,或者以新对象替换老对象。例如,利用对象展开运算符我们可以这样写:
state.obj = { ...state.obj, newProp: 123 }
文字很枯燥,我们还是来看个小栗子
{{num}}
// add方法执行之后页面会立即响应这个新的状态
{{this.$store.state.newNum || 0}}
mutations: {
add (state, payload) {
state.num+=payload.addNum
Vue.set(state, 'newNum', 1000) // 像仓库中的state新增加一个newNum状态,初始值为1000
// state= {...state, newNum: 2000} 这个方法不管用了,用下面的replaceState()方法
this.replaceState({...state, newNum: 2000})
}
}
-
Mutation 必须是同步函数
下面这种写法必须避免(直接官方例子加持):
mutations: {
someMutation (state) {
api.callAsyncMethod(() => {
state.count++
})
}
}
我们在模板文件中通过事件去操作mutations
时,如果mutations
中为异步函数,那么当 mutation
触发的时候,回调函数还没有被调用,因为我们不知道什么时候回调函数实际上被调用——实质上任何在回调函数中进行的状态的改变都是不可追踪的。
-
mapMutations
其实这几个语法糖的使用方法都差不多,这里就直接上官方栗子了。
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
,不同在于:
-
Action
提交的是mutation
,而不是直接变更状态。 -
Action
可以包含任意异步操作。前面说过mutation
只能包含同步事务,所以在处理异步事务就需要Action
,通过Action
控制了异步这一过程,之后再去调用mutation
里面的方法来改变状态。
先看官方的一个小栗子来认识它的基础语法:
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
Action
函数接受一个与 store
实例具有相同方法和属性的 context
对象,因此你可以调用 context.commit
提交一个 mutation
,或者通过 context.state
和 context.getters
来获取 state
和 getters
。那为什么这里的参数不能直接是store
本身呢,这就和Modules
有关了,了解Modules
之后就会发现store
总仓库下面可能会有很多个不同的模块仓库,而每一个不同的模块仓库都有自己的Action
、state
、mutation
、getter
,如果使用store
那么store.state
拿到的就不会是当前模块的state
,而context
可以理解为当前模块的store
,这样就不会引起冲突。
实践中,我们会经常用到 ES2015 的 参数解构 来简化代码(特别是我们需要调用 commit 很多次的时候):
actions: {
increment ({ commit }) { // 这里就是将context展开式写法 { commit } = context.commit
commit('increment')
}
}
-
分发 Action
Action
通过 store.dispatch
方法触发:
store.dispatch('increment')
乍一眼看上去感觉多此一举,我们直接分发 mutation
岂不更方便?实际上并非如此,还记得 mutation
必须同步执行这个限制么?Action
就不受约束!我们可以在 Action
内部执行异步操作。
上面这一块内容基本来源于官网,感觉官网对于 Action
函数的传参,分发等基本用法都说的比较详细,在这里我们只要记住一点Action
用来执行异步操作仓库状态的情况。我们还是来做个小栗子更直观了解吧
{{num}}
// store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
num: 10
},
mutations: {
add (state, addNum) {
state.num += addNum
}
},
actions: {
delayAdd (context) {
setTimeout(() => {
context.commit('add', 100)
}, 1000)
}
}
})
上面是一个Action中
最基础的用法,因为Mutation中
不能接受异步操作,所以我们先将要修改的东西放在Mutation
中,然后在Action
中等待1s之后去操作Mutation
进行提交来更新数据。至于分发Action
是我们在模板中通过 this.$store.dispatch('delayAdd')
来执行,这样我们就完成了一个基础的Action
闭环demo
。
-
Actions 支持同样的载荷方式和对象方式进行分发:
// 以载荷形式分发
store.dispatch('delayAdd', {
addNum: 100
})
actions: {
delayAdd (context, payload) {
setTimeout(() => {
context.commit('add', payload.addNum)
}, 1000)
}
}
// 以对象形式分发
store.dispatch({
type: 'delayAdd',
addNum: 100
})
这里copy一个官方购物车示例,涉及到调用异步 API 和分发多重 mutation
的栗子来给大家看看,主要是过一遍有个印象
actions: {
checkout ({ commit, state }, products) {
// 把当前购物车的物品备份起来
const savedCartItems = [...state.cart.added]
// 发出结账请求,然后乐观地清空购物车
commit(types.CHECKOUT_REQUEST)
// 购物 API 接受一个成功回调和一个失败回调
shop.buyProducts(
products,
// 成功操作
() => commit(types.CHECKOUT_SUCCESS),
// 失败操作
() => commit(types.CHECKOUT_FAILURE, savedCartItems)
)
}
}
注意我们正在进行一系列的异步操作,并且通过提交 mutation
来记录 action
产生的副作用(即状态变更)。
-
mapActions
Action
也有类似的语法糖,这里就不多赘述了,其实用法都是差不多的,直接官方栗子
import { mapActions } from 'vuex'
export default {
// ...
methods: {
...mapActions([
'increment', // 将 `this.increment()` 映射为 `this.$store.dispatch('increment')`
// `mapActions` 也支持载荷:
'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch('incrementBy', amount)`
]),
...mapActions({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.dispatch('increment')`
})
}
}
-
Action结合Promise
Action
通常是异步的,那么如何知道 Action
什么时候结束呢?我们这里假设一种情况,模板上有一个数据,同时有这个数据当前的状态说明,我们如果通过Action
去操作这个数据,那么数据改变之后我们希望这个数据的当前状态说明就马上更新。举个小栗子
姓名:{{userinfo.name}}
性别:{{userinfo.six}}
状态:{{defalutText}}
// store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
userinfo: {
name: '张三',
six: '男'
}
},
mutations: {
changeInfo (state, payload) {
state.userinfo.name = payload.name
state.userinfo.six = payload.six
}
},
actions: {
// 这里直接用参数解构的写法
changeInfo ({commit, state}, payload) { // 接收context(解构写法)和载荷payload
return new Promise((resolve, reject) => {
setTimeout(() => {
commit('changeInfo', {
name: payload.name,
six: payload.six
})
resolve("执行完毕,可以开始改变状态啦") // 成功之后即可直接在then的回调中接收
}, payload.time * 1000)
})
}
}
})
上面的代码基本就是先展示state
中存好的userinfo
信息,点击按钮之后会有一个time
的倒计时来告诉你几秒之后改变数据,改变之后再将文字改成"成功改变啦"。
其实上面这段代码是有小瑕疵的,因为我们在分发action
的changeInfo
函数中给的是一个定时器,没有任何延迟信息,所以5s之后返回来的参数和我们在changeButton()
中写好的改变button
状态的文字能正好对应上。而日常开发中通常这里会写请求函数到后端然后等待返回值,这个过程如果耽误1s
那么们changeButton()
中就会出现问题,它会优先执行this.buttonText = '成功改变啦'
。
而实际因为请求有时间延迟,可能多出1s
就会出现文字改变而上面的信息并未更新。这里特意留给我们自己思考,如何完善这个小bug
,其实答案很简单,就是在then()
的回调中去处理这段逻辑,具体的实现可以自行去了解哦,对Promise
不太了解的可以点击了解Promise。
-
组合Action
利用Promise来实现组和Action
我们先直接上官网的demo小栗子,通过栗子看更明白
actions: {
actionA ({ commit }) {
return new Promise((resolve, reject) => {
setTimeout(() => {
commit('someMutation')
resolve()
}, 1000)
})
}
}
其实上面的这个小栗子在我们上一块 Action
结合Promise
中就说的很明白了,它就是想告诉我们通过Promise.resolve()
之后我们就知道这个时候actionA
就执行完毕了,我们就可以通过链式调用的then()
方法来走下一个流程啦。看官方给的actionB
actionB ({ dispatch, commit }) {
return dispatch('actionA').then(() => {
commit('someOtherMutation')
})
}
vue
模板中调用
store.dispatch('actionA').then((res) => {
// ... 这里执行actionA中异步结束之后的程序,可以接受res为actionA中resolve("将返回结果返回出去")
})
利用 async / await来实现组和Action
async
和await
是ES7中推出的新语法,直接看官方的栗子,简单明了
actions: {
async actionA ({ commit }) {
commit('gotData', await getData())
},
async actionB ({ dispatch, commit }) {
await dispatch('actionA') // 等待 actionA 完成
commit('gotOtherData', await getOtherData())
}
}
一个 store.dispatch 在不同模块中可以触发多个 action 函数。在这种情况下,只有当所有触发函数完成后,返回的 Promise 才会执行。
Module
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store
对象就有可能变得相当臃肿。
为了解决以上问题,Vuex
允许我们将 store
分割成模块(module
)。每个模块拥有自己的state
、mutation
、action
、getter
、甚至是嵌套子模块——从上至下进行同样方式的分割,我们先来做个简单的栗子,这里我们就不直接在index.js
里面新建module
了,因为实际开发中我们也是将模块单独拎出来,这样更有利于代码维护。如下栗子:
// moduleA.js
const moduleA = {
state: {
userInfo: {
name: 'alen',
age: 20
}
},
mutations: { ... },
getters: { ... },
actions: { ... }
}
export default moduleA
// moduleB.js
const moduleB = {
state: {
userInfo: {
name: 'nemo',
age: 32
}
},
mutations: { ... },
getters: { ... },
actions: { ... }
}
export default moduleB
在store
仓库的跟文件index.js
中引入模块A和模块B
import Vue from 'vue'
import Vuex from 'vuex'
import moduleA from './modules/moduleA'
import moduleB from './modules/moduleB'
Vue.use(Vuex)
export default new Vuex.Store({
state: { ... },
modules: {
ma: moduleA,
mb: moduleB
}
})
上面的代码就让我们轻松完成了模块的新建和引入,如果我们想在组件里访问模块A中的name
:
{{this.$store.state.ma.userInfo.name}} // alen
{{this.$store.state.mb.userInfo.name}} // nemo
上面的代码我们不难猜出,起始ma
和mb
的state
都是挂载在根节点的state
下面。所以我们在组件中直接访问模块中的state
需要先去访问根节点的state
然后在加上模块名及对应的参数。
-
模块的局部状态
对于模块内部的 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
:
const moduleA = {
// ...
actions: {
// 这里用的解构写法,实际上是context.state, context.commit, context.rootState
incrementIfOddOnRootSum ({ state, commit, rootState }) {
if ((state.count + rootState.count) % 2 === 1) {
commit('increment')
}
}
}
}
对于模块内部的 getter
,根节点状态会作为第三个参数暴露出来:
const moduleA = {
// ...
getters: {
sumWithRootCount (state, getters, rootState) {
return state.count + rootState.count
}
}
}
这里我们直接举一个组件操作模块B进行mutation
提交的小栗子
{{this.$store.state.mb.userInfo.name}}
{{this.$store.state.mb.userInfo.age}}
// moduleB.js
const moduleB = {
state: {
userInfo: {
name: 'nemo',
age: 32,
hobby: ['football', 'basketball', 'badminton', 'volleyball']
}
},
mutations: {
editAge (state, payload) {
state.userInfo.age = payload.age
}
}
}
export default moduleB
看上面的代码会发现我们通过 commit
提交模块B
里面的内容并没有使用 mb
这个模块名,而是直接全局提交就能进行修改,我们再来看看 getters
是不是也是直接可以全局提交修改。
{{item}}
const moduleB = {
state: {
userInfo: {
name: 'nemo',
age: 32,
hobby: ['football', 'basketball', 'badminton', 'volleyball']
}
},
getters: {
filteHobby (state, getters, rootState) {
return state.userInfo.hobby.filter(item => item != 'football')
}
}
}
export default moduleB
果然 getters
也可以直接通过 this.$store.getters
来操作,而不需要再加上它所在的模块名来进行调用,这里我们来看看官方是这样说的:
默认情况下,模块内部的
action
、mutation
和getter
是注册在全局命名空间的——这样使得多个模块能够对同一mutation
或action
作出响应。
-
命名空间
如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true
的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter
、action
及 mutation
都会自动根据模块注册的路径调整命名。直接来看小栗子:
// 新建模块C
const moduleC = {
namespaced: true,
state: {
userInfo: {
name: 'kity',
age: 10,
list: [1, 2, 3, 4]
}
},
getters: {
filterList (state) {
return state.userInfo.list.filter(item => item != 1)
}
},
mutations: {
changeAge (state, payload) {
state.userInfo.age = payload.age
}
},
actions: {...}
}
export default moduleC
// index.js中导入
import moduleC from './modules/moduleC'
export default new Vuex.Store({
modules: {
mc: moduleC
}
})
// 模板文件
{{this.$store.state.mc.userInfo.name}}
{{this.$store.state.mc.userInfo.age}}
{{item}}
通过 commit
提交 moduleC
中的 changeAge()
,这时候的写法如下:
通过 getters
获取 moduleC
中的 filterList()
,一种是返回值直接通过 this.$store
去写,一种是语法糖的写法
当然模块里面再嵌套模块也可以,路径要不要多走一层主要看你的 namespaced: true
有没有声明,这里贴一下官方的代码:
const store = new Vuex.Store({
modules: {
account: {
namespaced: true,
// 模块内容(module assets)
state: () => ({ ... }), // 模块内的状态已经是嵌套的了,使用 `namespaced` 属性不会对其产生影响
getters: {
isAdmin () { ... } // -> getters['account/isAdmin']
},
actions: {
login () { ... } // -> dispatch('account/login')
},
mutations: {
login () { ... } // -> commit('account/login')
},
// 嵌套模块
modules: {
// 继承父模块的命名空间
myPage: {
state: () => ({ ... }),
getters: {
profile () { ... } // -> getters['account/profile']
}
},
// 进一步嵌套命名空间
posts: {
namespaced: true,
state: () => ({ ... }),
getters: {
popular () { ... } // -> getters['account/posts/popular']
}
}
}
}
}
})
启用了命名空间的 getter
和 action
会收到局部化的 getter
,dispatch
和 commit
。换言之,你在使用模块内容(module assets
)时不需要在同一模块内额外添加空间名前缀。更改 namespaced
属性后不需要修改模块内的代码。
-
在带命名空间的模块内访问全局内容
如果你希望使用全局 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'
},
someOtherGetter: state => { ... }
},
actions: {
// 在这个模块中, dispatch 和 commit 也被局部化了
// 他们可以接受 `root` 属性以访问根 dispatch 或 commit
someAction ({ dispatch, commit, getters, rootGetters }) {
getters.someGetter // -> 'foo/someGetter'
rootGetters.someGetter // -> '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
若需要在带命名空间的模块注册全局 action
,你可添加 root: true
,并将这个 action
的定义放在函数 handler
中。例如:
{
actions: {
someOtherAction ({dispatch}) {
dispatch('someAction') // 通过dispatch直接调用不需要加上命名空间
}
},
modules: {
foo: {
namespaced: true,
actions: {
someAction: { // 全局的action
root: true,
handler (namespacedContext, payload) { ... } // -> 'someAction'
}
}
}
}
}
-
在模块里面使用辅助函数mapState、mapGetters、mapMutations和mapActions
由于存在命名空间,在组件里面采用上面的写法会出现问题,这里要想使用辅助函数来映射模块里面的东西需要指定空间名称来告诉辅助函数应该去哪儿找这些。 这儿我以上面我的C模块为例,首先对于 mapSatate
函数可以这样玩,我在全局的 modules
里面声明了 mc
,那我的空间名称就是 mc
:
computed: {
...mapState('mc', ['name', 'desc']) // 这里模块里面要使用辅助函数的话要多传一个参数才行
}
然后在模版里面写 name
,desc
即可,或者可以这样:
computed: {
...mapState('mc', {
name(state) {
return state.name;
},
desc(state) {
return state.desc;
}
})
}
mapActions
、mapMutations
、mapGetter
都可以向上面一样类似写法,这里我们写一个mapMutations
的栗子参考
如果你确实不想在每个辅助函数里写空间名称,Vuex
也提供了其它办法,使用createNamespacedHelpers
创建基于某个命名空间辅助函数,它返回一个对象,对象里有新的绑定在给定命名空间值上的组件绑定辅助函数:
import { createNamespacedHelpers } from 'vuex';
const { mapState, mapMutations } = createNamespacedHelpers('mc');
这样你在写辅助函数的时候就不需要单独指定空间名称了。 其它类似,就不再赘述了!其实 vuex
官网中对于 Module
这个板块还有几个知识点,只是对于了解基础的话过多的深入可能还会影响自己的消化进度,如果当我们做一个项目庞大到需要建立很多个模块,然后模块中又进行嵌入,那么相信我们对 vuex
已经基本都了解了,到时候再去查阅相关的进阶资料也很容易理解。
结语
vuex
的几个核心概念的基本认识就都在这里了,本文也主要是参考了官网的文档进行归纳总结。当然本篇相当于基础入门篇,实际开发中使用 vuex
肯定远远比这个复杂,但是万丈高楼平地起,希望对大家有所帮助,至于其他进阶内容大家有兴趣进官网浏览,也可查阅相关的资料进行学习。如果文章有理解不到位的地方,还请各位多批评指正!