写在前面
作为一个Vue用户,大家想必已经厌倦了props、bus的复杂和繁琐。所以今天我们来体会一下Vuex,在降低耦合度的同时还增加了代码的可维护性。可谓是一举两得。同时由于其简单的语法和较少的内容,也适合大多数使用vue-cli的童鞋
准备
在安装之前建议你们去chrome商城中找一个名为“Vue.js devtools”的插件,可以跟踪数据的变化。
开始
state
使用Vuex的时候,我们必须先引入一个概念:“仓库”
- 顾名思义,store即容器,包含你应用中的状态(state)。而state中存储的则是我们希望共享的变量
const store = new Vuex.Store({
state: {
count: 0
}
})
而在.Vue文件中我们可以通过computed属性来获取store中定义的变量
const Counter = {
template: `<div>{{ count }}</div>`,
computed: {
count () {
return store.state.count
}
}
}
每当store.state.count发生变化我们都会重新求取computed的值并触发更新相关的DOM
getters
- 还有一个问题:我们可以添加数据,可以处理数据,但谁来获取?总不能你窝里一顿操作人家外边照样风平浪静吧?所以我们再引入一个新的属性:getters
getters: {
// getter接受state作为其第一个参数(毕竟返还的也是state中定义的对象)
getNum(state) {
return state.num
}
}
getters
中的方法可以将数据返还给组件,以完成通信。我们可以在.vue
文件中通过this.$store.getters.method
完成对应值的获取
computed:{
appShowNum(){
return this.$store.getters.getNum
}
}
//我们可以理解为,Getter会暴露名为store.getters的对象。我们可以通过属性的形式访问这些值
就像计算属性一样 getter
的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
getter
我们可以将其理解为是store
的计算属性。其返回值就像computed一样,会被缓存起来,只有发生改变时才会重新被计算
mutaions
- 只有数据当然远远无法满足我们的要求啦,Vuex同样贴心的给我们提供了另一个工具:mutations。此工具用以实现对数据的操作。后边我们还要介绍一个名为actions的属性,他们俩的区别就是:mutaions必须是同步处理方式
我们来举个例子
mutations: {
// payload是一个对象,官方文档称其为“载荷”
addNum(state, payload) {
state.num += payload.num
}
},
可以看到,我们可以传入一个对象payload,按照此题来看也就是一个具有num属性的对象。
当然,还有另外一个要注意的点:我们不能直接调用一个mutaion函数,而是要走一个流程:store.commit('functionName')
actions
- 异步怎么办?
上边我们说到Vue.js devtools对于mutaions只能检测到其同步请求,而对于异步请求则无法同步快照(没记录,只有结果)。但是我们的项目中更多的是异步操作。比如我们在数据同步的时候需要后台给我们发送的数据,故需要异步的ajax请求,数据返回的时候才能完成加载。这时候我们就可以用到新的属性:actions
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
// payload是一个对象,官方文档称其为“载荷”
addNum(state, payload) {
state.num += payload.num
}
},
actions: {
// 此处有异步,但没有关系,可以记录快照
addNumByServerRate(store, payload) {
setTimeout(() => {
// 后台比例是1
let rate = 2
// 最终还是通过mutations更改数据
//使用store.commit方法触发状态的变更
store.commit('addNum', { num: payload.num * rate })
}, 1000)
}
}
})
要注意两点:
- Acion提交的是mutataion,而非直接变更状态
- Action可以包含任意异步操作
分发Acion
作为action
的一个重要的方法,dispatch
主要用来将action
中的方法分发到mutation
中去。只有如此,action
的异步方法才可以在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)
)
}
}
这是一个购物车示例。我们异步调用了types
的CHECKOUT_REQUEST
API,同时还做了分发多重mutation
再结合ES6的await
和async
// getData() 和 getOtherData() 返回的是 Promise
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模块化
由于Vuex使用单一状态树,所以随着应用的增大,store对象也会变得相当臃肿。所以我们不妨将store分割成一个个的模块,并令其拥有自己的state、mutation、action、getter、甚至是潜嵌套模块
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 的状态
至于模块内部,state
则指向了模块本身,即,此state
是模块的局部状态,故不用担心混淆。若涉及到多层嵌套则可以通过rootState
来完成查询
这里不同的方法有不同的默认接收参数
- mutation(state)
- getter(state)
- action(state, commit, rootState)
- getter(state, getters, rootState)
用的时候注意区分,别搞混了