基于Vuex从零实现本身的Vuez插件-actions(四)

到目前为止,咱们已经实现了vuex中的getters,mutations,接下来就该轮到咱们的actions了。
在具体实现actions以前,咱们必须明确actions的功能和用途.javascript

详情能够参考vuex-actionshtml

经过官网的介绍,咱们总结得出,在actions中,咱们主要作两件事,一件是commit mutaions,另外一件是dispatch other actionsvue

为了可以完成以上两件任务,咱们必须使得在actions中可以访问到store中的commitdispatch方法。java

将actions绑定到store

这步操做和getters/mutations中彻底同样,有须要注意的地方在代码中给出了注释vuex

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,ide

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方法而报错。ui

解决this指向问题

一个比较简单的解决方法是,在构造方法中绑定this,这样就不会找不到print方法了。this

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 符合预期
复制代码

未完待续...

相关文章
相关标签/搜索