到目前为止,我们已经实现了vuex中的getters,mutations,接下来就该轮到我们的actions了。
在具体实现actions之前,我们必须明确actions的功能和用途.
详情可以参考vuex-actions
通过官网的介绍,我们总结得出,在actions中,我们主要做两件事,一件是commit mutaions,另一件是dispatch other actions
为了能够完成以上两件任务,我们必须使得在actions中能够访问到store中的commit和dispatch方法。
将actions绑定到store中
这步操作和getters/mutations中完全一样,有需要注意的地方在代码中给出了注释
import Vue from 'Vue'
class Store{
constructor(options){
this._vm = new Vue({
data:options.state
})
...//getters
...// mutations
// actions
let actions = options.actions || {}
this.actions = {}
Object.keys(actions).forEach((key)=>{
this.actions[key] = (payload) =>{
actions[key](this,payload) // 在这里传入this使为了让我们能够访问到commit和dispatch方法
}
})
get state(){
return this._vm
}
// 在这里,我们实现一个dispatch方法
dispatch(actionType,payload){
this.actions[actionType](payload)
}
commit(mutationType,payload){
this.mutations[mutationType](payload)
}
}
接下来,让我们注册一个简单的actions,
const store = new Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment ({commit,dispatch}) {
commit('increment')
}
},
getters:{
getCount(state){
return state.count
}
}
})
现在我们注意一下,actions中的increment的执行流程
// options中的actions绑定到了store实例上
// actions['increment'](this) //执行
// {commit,dispatch} = this // 结构赋值
// commit('increment')
// this.mutations['increment'] 这里会出现问题,this此时并不是指`store`实例,而是指向undefined
this指向
类的方法内部如果含有this,它默认指向类的实例。但是,必须非常小心,一旦单独使用该方法,很可能报错。
class Logger {
printName(name = 'there') {
this.print(`Hello ${name}`);
}
print(text) {
console.log(text);
}
}
const logger = new Logger();
const { printName } = logger;
printName(); // TypeError: Cannot read property 'print' of undefined
上面代码中,printName方法中的this,默认指向Logger类的实例。但是,如果将这个方法提取出来单独使用,this会指向该方法运行时所在的环境(由于 class 内部是严格模式,所以 this 实际指向的是undefined),从而导致找不到print方法而报错。
解决this指向问题
一个比较简单的解决方法是,在构造方法中绑定this,这样就不会找不到print方法了。
class Logger {
constructor() {
this.printName = this.printName.bind(this);
}
// ...
}
另一种解决方法是使用箭头函数。
class Obj {
constructor() {
this.getThis = () => this;
}
}
const myObj = new Obj();
myObj.getThis() === myObj // true
箭头函数内部的this总是指向定义时所在的对象。上面代码中,箭头函数位于构造函数内部,它的定义生效的时候,是在构造函数执行的时候。这时,箭头函数所在的运行环境,肯定是实例对象,所以this会总是指向实例对象。
因此我们必须想办法保障this是指向store实例的
在这里,我们将commit,dispatch声明为箭头函数,因为箭头函数中的this在创造时就被确定了,而不会随着上下文环境而发生变化。
dispatch = (actionType,payload) =>{
this.actions[actionType](payload)
}
commit = (mutationType,payload) =>{
this.mutations[mutationType](payload)
}
此时完整代码为
class Store {
constructor(options) {
this.data = options.state;
let getters = options.getters || {}
this.getters = {}
// mutations
let mutations = options.mutations || {}
this.mutations = {}
Object.keys(mutations).forEach((key) => {
this.mutations[key] = (payload) => {
mutations[key](this.state, payload)
}
})
// 把getter对象上的属性全部绑定到this.getter上
Object.keys(getters).forEach((key) => {
Object.defineProperty(this.getters, key, {
get: () => getters[key](this.state)
})
})
let actions = options.actions || {}
this.actions = {}
Object.keys(actions).forEach((key) => {
this.actions[key] = (payload) => {
actions[key](this, payload) // 在这里传入this使为了让我们能够访问到commit和dispatch方法
}
})
}
get state() {
return this.data
}
dispatch = (actionType, payload) => {
this.actions[actionType](payload)
}
commit = (mutationType, payload) => {
this.mutations[mutationType](payload)
}
}
通过上面的代码,我们发现在实现getters,mutations,actions的绑定时,逻辑都是一样的,我们可以将其抽取出来,封装成一个函数。
let forEach = (obj,callback)=>{
Object.key(obj).forEach(key=>{
callback(key,obj[key])
})
}
使用封装函数,修改我们的代码:
let forEach = (obj,callback)=>{
Object.key(obj).forEach(key=>{
callback(key,obj[key])
})
}
class Store {
constructor(options) {
this.data = options.state;
let getters = options.getters || {}
this.getters = {}
// mutations
let mutations = options.mutations || {}
this.mutations = {}
forEach(getters,(key,fn)=>{
Object.defineProperty(this.getter,key)=>{
get:()=>{
return fn(this.state)
}
}
})
forEach(mutations,(key,fn)=>{
this.mutations[key] = (payload)=>{
fn((this.state, payload))
}
})
let actions = options.actions || {}
this.actions = {}
forEach(actions,(key,val)=>{
this.actions[key] = (payload)=>{
fn(this,payload)
}
})
}
get state() {
return this.data
}
dispatch = (actionType, payload) => {
this.actions[actionType](payload)
}
commit = (mutationType, payload) => {
this.mutations[mutationType](payload)
}
}
测试是否成功
store.dispatch('increment')
console.log(store.getters.getCount) // 输出1 符合预期
未完待续...