基於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 符合預期
複製代碼

未完待續...

相關文章
相關標籤/搜索