我看Vuex(一)

煎蛋_發表於2018-02-21

之前閱讀過 redux 的原始碼,vuex 同樣也是一個狀態管理工具,之前開發 vue 應用的時候,用到的 vuex 也比較多,所以自然對 vuex 頗有興趣,最主要的原因是 我愛學習,(不好意思,臉掉地上了),,哈哈哈哈哈哈

images

(其實過年這幾天也挺無聊的。。。

接下來我們來回歸正題

default

Vuex 是如何引入到 Vue 中的

我們都知道,在 Vue 中使用 Vuex 必須通過 Vue.use(Vuex) 方法來使用,所以先來瞄一瞄 Vue 對 Vuex 做了什麼不可告人的事,驚恐.gif

來到 vue 原始碼下的 core/global-api/use.js

Vue.use = function (plugin: Function | Object) {
        // 檢測該外掛是否已經被安裝
        if (plugin.installed) {
            return
        }
        // use 支援傳入一個選項物件,如
        // Vue.use(MyPlugin, { someOption: true })
        const args = toArray(arguments, 1)  //獲取可選物件
        
        args.unshift(this)  // 將Vue 構造器傳入,當外掛的第一個引數
        if (typeof plugin.install === 'function') {
            // install執行外掛安裝
            plugin.install.apply(plugin, args)
        } else if (typeof plugin === 'function') {
            plugin.apply(null, args)
        }
        plugin.installed = true  // 防止再次安裝
        return this
    }
複製程式碼

所以,vue 對外掛還是挺溫柔的,只用了它的一個安裝函式進行外掛的安裝。好,我們來看看 vuex 的 install 函式 在 store.js 的最下面,我們看到了它的身影

function install(_Vue) {
  if (Vue && _Vue === Vue) {
    // 給出警告:說明重複安裝
    return
  }
  Vue = _Vue
  applyMixin(Vue)
}
複製程式碼

這裡我們也可以看到 vuex 自己內部也做了一個限制,防止重複安裝,最後該函式呼叫了 applyMixin(Vue)

咻一下,我們來到 mixin.js,該函式也很簡單,先是獲取 Vue 的版本號,如果版本是 2.0 以上版本,就使用 Vue.mixin 來在所有元件中混入 beforeCreate 生命鉤子,對應的處理函式是 vuexInit,反之就向後相容 1.x 版本

function vuexInit() {
        // this -> vm
        const options = this.$options
        // store injection
        if (options.store) { // 獲取傳入new Vue({store}) 裡面的 store,並註冊為 vm 的 $store 屬性,這樣我們就能在例項中使用 this.$store 訪問 store 物件了
            this.$store = typeof options.store === 'function' ?
                options.store() :
                options.store
        } else if (options.parent && options.parent.$store) {
            // 子元件從其父元件引用 $store 屬性
            this.$store = options.parent.$store
        }
    }
複製程式碼

至此,前菜已經上齊了

主角 Store 閃亮登場

835ab1e97a

store.js,別看長長一竄,有500多行,其實看進去了,你會感覺,也沒啥可怕嘛 該檔案主要由一個 Store 類和一些輔助函式構成,我們先來看 這個構造類

該建構函式中首先是進行一些必要的判斷,如瀏覽器環境下自動安裝、是否已經安裝了 Vue、是否支援 Promise,是否已經例項化了 Store 建構函式,其中用到了 assert 斷言函式

// 建構函式
    constructor(options = {}) {
        // .......
    }

// util.js
   function assert (condition, msg) {
    if (!condition) throw new Error(`[vuex] ${msg}`)
  }
複製程式碼

然後是從傳入的options 中提取出 pluginsstrict,並做一些變數的初始化

// store internal state
    // 表示提交狀態,作用是保證對 Vuex 中 state 的修改只能在 mutation 的回撥函式中,而不能在外部隨意修改state
    this._committing = false
    // 使用者定義的 actions
    this._actions = Object.create(null)
    // action 訂閱者
    this._actionSubscribers = []
    // // 使用者定義的 mutations
    this._mutations = Object.create(null)
    // 使用者定義的 getters
    this._wrappedGetters = Object.create(null)
    // 收集使用者定義的 modules
    this._modules = new ModuleCollection(options)
    // 模組名稱空間map
    this._modulesNamespaceMap = Object.create(null)
    // 儲存所有對 mutation 變化的訂閱者,當執行commit時會執行佇列中的函式
    this._subscribers = []
    // 建立一個 Vue 例項, 利用 $watch 監測 store 資料的變化
    this._watcherVM = new Vue()
複製程式碼

接著後面是對 dispatch 和 commit 函式中的 this 的重新繫結

const store = this
    const {
      dispatch,
      commit
    } = this

    this.dispatch = function boundDispatch(type, payload) {
      return dispatch.call(store, type, payload)
    }
    this.commit = function boundCommit(type, payload, options) {
      return commit.call(store, type, payload, options)
    }
複製程式碼

這樣做是因為在元件中通過 this.$store 直接呼叫 dispatch/commit 方法時, 能夠使 dispatch/commit 方法中的 this 指向當前的 store 物件而不是當前元件的 this。
我們知道 new Vue 時會把傳入的物件的中的 this 繫結為 vm,例如 computed 屬性,裡面我們寫如下程式碼時,會把計算屬性裡面的 this 繫結為當前的 vm 例項

 computed: {
    // 計算屬性的 getter
    reversedMessage: function () {
      // `this` 指向 vm 例項
      return this.message.split('').reverse().join('')
    }
  }
複製程式碼

( 上面這段如有不妥,歡迎指出,謝謝 ^_^

接著就是 安裝模組,vm 元件設定,傳入外掛以及 devtoolPlugin 外掛的設定了

    this.strict = strict

    const state = this._modules.root.state

    // init root module.
    // this also recursively registers all sub-modules
    // and collects all module getters inside this._wrappedGetters
    installModule(this, state, [], this._modules.root)

    // initialize the store vm, which is responsible for the reactivity
    // (also registers _wrappedGetters as computed properties)
    resetStoreVM(this, state)

    // apply plugins
    plugins.forEach(plugin => plugin(this))

    if (Vue.config.devtools) {
      devtoolPlugin(this)
    }
複製程式碼

接下來我們先不講 dispatchcommit 是怎麼實現的,先來重點關注下 modules 這部分,畢竟前面已經 new ModuleCollection(options),先來後到嘛,,哈哈

由於現在的篇幅也算可以了,怕大家看到長篇大論頭疼,所以我們轉移陣地。

1

(如有不當,歡迎指出,謝謝 ^_^

原文地址: 我看Vuex(一)

相關文章