幾十行程式碼實現一個vue的狀態管理

Jike發表於2019-01-28

介紹

採用集中式儲存管理應用的所有元件的狀態, 就能實現元件間資料共享

實現

邏輯圖

幾十行程式碼實現一個vue的狀態管理

從圖上有兩條線: Vue.use(vuec), 與 new Vuec.center(options)

第一條線Vue.use(vuec)安裝外掛

使用Vue.use(vuec)時, 會執行vuecinstall方法,會注入引數Vue 所以vuec是這樣的,

// index.js
import {Center, install} from './center'
export default {Center, install}
複製程式碼

Center物件將例項化成center(下面再說),我們先看看install方法

// center.js
let Vue // 全域性變數, 儲存install裡的Vue
export function install (_Vue) {
  if (!Vue) {
    _Vue.mixin({
      beforeCreate: applyMixin // 在beforeCreate鉤子上混入applyMixin函式
    })
  }
  Vue = _Vue
}
複製程式碼

installVue原型的beforeCreate混入applyMixin函式, 也就是說在生成每個Vue元件時,在它的生命週期beforeCreate鉤子上就會執行applyMixin方法

第二條線 new Vuec.center(options)例項化Center物件

先看看使用者傳入的options, 下面例子

export default new Vuec.Center({
  state: {
    name: 'liuyang'
  },
  mutations: {
    changeName (state) {
      state.name = 'jike'
    }
  }
})
複製程式碼

上面程式碼會生成center例項, 該例項上應該包括:state狀態,commit方法提交變更等

// center.js
export class Center {
  constructor (options= {}) {
    let center = this
    this.mutations = options.mutations
    observeState(center, options.state)
  }
  get state () {  // 代理了this.$center.state的最終訪問值
    return this._vm.$data.$$state
  }
  commit (_type, _payload) {
    this.mutations[_type](this.state, _payload)
  }
}
function observeState(center, state) { // 響應式state
  center._vm = new Vue({
    data: {
      $$state: state
    }
  })
}
複製程式碼

在執行new Vuec.Center({..})時,就是執行Center的建構函式

  1. 首先執行let center = this, 定義center儲存當前例項

  2. 接著執行this.mutations = options.mutations, 在例項center上新增mutations屬性, 值就是使用者輸入mutations,

    按上面例子, this.mutations長成這樣

    this.mutations = {
        changeName (state) {
          state.name = 'jike'
        }
    }
    複製程式碼
  3. 最後執行observeState(center, options.state), 作用:讓center例項的state屬性指向options.state並且是響應式的

    function observeState(center, state) { // 響應式state
      center._vm = new Vue({  // 利用Vue的響應系統,將state轉化成響應式
        data: {
          $$state: state
        }
      })
    }

複製程式碼

center例項上新增_vm屬性, 值是一個Vue例項, 在該Vue例項的data下定義了$$state, 它的值是options.state使用者輸入的state; 結合上面的這段程式碼

// center.js
export class Center {
  ...省略
  get state () {  // 代理了this.$center.state的最終訪問值
    return this._vm.$data.$$state
  }
  ...省略
}
複製程式碼

所以我們在元件中訪問center.state其實就是訪問center._vm.$data.$$state

OK, center就構建好了

建立Vue元件

使用者輸入

import Vue from 'vue'
import App from './App'
import router from './router'
import center from './center'

new Vue({
  el: '#app',
  router,
  center, // 構建好的center例項
  template: '<App/>',
  components: {App}
})
複製程式碼

beforeCreate生命週期時會觸發上面混入的applyMixin函式

// mixins.js
export default function applyMixin() {
  vuecInit.call(this) // 
}

function vuecInit () {
  const options = this.$options
  // vue的例項化是從外往內, 所以父元件的$center一定是options的center
  this.$center = options.parent?options.parent.$center: options.center
}
複製程式碼

applyMixin裡會執行vuecInit.call(this), 這裡的this指向當前元件的例項,

接著看vuecInit, 定義了options等於使用者輸入選項,因為先建立根元件, 所以根元件this.$center的值的引用就是我們在new Vue({..center})時傳入的center例項, 下面所有元件都指向它

OK, 你就可以在元件裡使用this.$center訪問了

commit變更

// center.js
export class Center {
  ... 省略
  commit (_type, _payload) {
    this.mutations[_type](this.state, _payload)
  }
}
複製程式碼

通常我們變更時: this.$center.commit('changeName', 'jike'), 這樣的話, this.mutations[_type]就是對應方法函式, 往該函式裡傳入state以及payload,

舉上面的例子

// this.mutations[_type] , _type = 'changeName', payload= 'jike'
this.mutations = {
    changeName (state, payload) {
      state.name = payload
    }
}

複製程式碼

說明

上面只是一個簡單的狀態管理, 還有很多地方沒有實現: actions非同步變更,getters函式,modules模組分割, 輔助函式mapState..

原始碼地址: github

相關文章