Vuex2.0源碼淺析

一、什麼是Vuex?

Vuex是一個專門爲Vue.js應用程序開發的狀態管理模式。它採用集中式存儲管理應用的全部組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。通俗一點理解即:針對組件繁多交互複雜的單頁面應用,Vuex提供了一種便利、準確和可預測的狀態管理方式,方便組件之間的數據共享和修改。vue

二、Vuex核心概念

  • State:即狀態,也就是Vuex核心管理的對象;vuex

  • Getters:派生狀態,對state的二次包裝(eg:默認後端時間戳轉化爲日期格式),Getters裏的方法全部組件均可以使用;後端

  • Mutations:全部修改狀態都是經過提交mutation,mutation相似事件,定義事件類型和回調函數,而回調函數就是進行狀態修改的地方,狀態修改必定是同步進行,從而確保狀態修改能夠被追蹤到;api

  • Actions:跟Mutations惟一不一樣的是進行異步修改狀態,本質是在回調提交Mutation;promise

  • Modules:爲了解決狀態樹龐大store臃腫問題,提出module概念,分化store到每一個module,每一個module都是一個小store。app

三、Vuex2.0源碼結構

本片文章介紹Vuex2.0源碼部分,經過簡單介紹瞭解背後的運行機制,讓咱們在使用更清楚其中的原理。首先看下整個源碼構成,以下圖:異步

clipboard.png

Vuex源碼部分總共包括五個部分:函數

  • install:安裝部分源碼;工具

  • store:源碼核心部分,本文重點介紹內容;學習

  • api:源碼提供一些內部和外部api;

  • 輔助函數:語法糖,讓咱們在使用Vuex的時候書寫更爲簡便;

  • plugin:提供一些默認插件,咱們也能夠自定義擴展插件的書寫。

四、源碼淺析

4.一、install

安裝部分源碼核心目的:給Vue注入一個Store屬性。
整體流程以下:

clipboard.png

核心源碼部分:

function vuexInit () {
    const options = this.$options
    // store 注入
    if (options.store) {
      this.$store = options.store
    } else if (options.parent && options.parent.$store) {
      this.$store = options.parent.$store
    }
}

4.二、store

store是整個Vuex的核心內容,其餘幾個部分的源碼都是爲了支撐store而存在的,store中完成全部組件共享數據的註冊和調用修改方法。首先經過一個流程圖看下store構造函數的構成:
圖片描述

store像是一個生態環境,整個應用裏的每一個組件都要在這個環境裏註冊,完成狀態的可預測管理。註冊以前須要進行環境檢測,這像是進入這個生態環境註冊的門票,或者通行證;達到註冊標準以後,進行一些內部屬性和方法的初始化工做,這像是爲註冊工做搭建一個「舞臺」,接下來的註冊工做都在這個「舞臺」上完成。

4.2.一、installModule

安裝模塊部分源碼主要完成模塊的state、mutations、actions和getters的註冊工做,先整體看下源碼構成:

clipboard.png

模塊安裝初期針對一些內部api的註冊工做,接下來是state的更新工做,更新邏輯以下:

clipboard.png

熱更新:在註銷一個module的時候,其邏輯是安裝一個空module達到更新的狀態,而這個更新即爲熱更新。熱更新的狀態修改單獨處理。

在獲取父節點的狀態以後,進行一次基於父節點狀態的commit提交修改便可完成模塊的state更新到state tree。源碼以下:

if (!isRoot && !hot) {
    const parentState = getNestedState(rootState, path.slice(0, -1))
    const moduleName = path[path.length - 1]
    store._withCommit(() => {
      Vue.set(parentState, moduleName, module.state)
    })
}

接下來是一次store本地化的工做,即完成dispatch、commit、getters和state的本地化工做。本地化的目的是爲了更好的進行數據方法操做。
mutations註冊邏輯和actions註冊邏輯幾近類似,邏輯以下:

clipboard.png

在store環境裏有兩個屬性:_mutations和_actions分別用來存儲模塊定義的mutations和actions,依據當前模塊的類型進行查找對應的內部屬性對象,而且將模塊對應的回調函數進行包裝插入到對應屬性對象裏,到此即完成註冊工做。兩者差異在於包裝回調函數處理上的不一樣,mutation直接將回調包裝起來便可,action對於回調函數的結果進行Promise對象的處理,而後包裝。
getters的註冊邏輯以下:

clipboard.png

getters是爲了獲取派生狀態,所以不容許重名,首先根據模塊類型進行重名斷定,斷定的依據來自內部屬性:_wrappedGetters,這個屬性存儲着整個應用的註冊getter。接下來就是將模塊的getters按照類型存入即完成註冊。最後是一個子模塊的遞歸調用的方法。
installModule只是完成了模塊的註冊工做,離咱們可使用這些狀態還有一些須要處理的代碼。

4.2.二、resetStoreVM

這個方法是對state和getters進行最後處理,以致於讓用戶能夠調用這些狀態。源碼邏輯以下:

clipboard.png

核心內容是store._vm這樣一個內部變量,本質上將註冊後的state和getters做爲新的數據源實例化一個Vue對象傳遞給store._vm,而且刪除舊的store._vm。與此同時,定義store.getters.xxx=store._vm[xxx],從而完成使用getters的正確姿式。state的使用是由store內部提供了一個api(store._vm.data.$$state.xxx),在更新store._vm以後,就能夠訪問這個模塊的state。
mutations和actions使用經過store內部提供的兩個重要api來實現,接下來介紹一個重要api。

五、api

5.1commit 和 dispatch

這兩個api分別是用來完成mutations和actions的使用工做。源碼邏輯以下:

圖片描述

commit的邏輯是:從上面註冊過的內部屬性對象裏拿到對應的mutations,而後經過_withCommit提交包裝回調函數便可,同時使用內部api subscribe進行狀態修改追蹤訂閱。而dispatch則是拿到註冊的actions,而後promise.all執行回調,回調裏則是進行commit提交。

5.2_withCommit

_withCommit (fn) {
    const committing = this._committing
    this._committing = true
    fn()
    this._committing = committing
 }

這個內部api是每次提交狀態修改的核心源碼,其邏輯很簡單,在每次執行狀態修改的時候,保證內部屬性_committing爲true,而這個屬性的默認初始值爲false。這樣在追蹤狀態變化的時候,若是_committing不爲true,那麼認爲此次的修改是不正確的。

六、輔助函數

Vuex除了上述提供的api之外,還提供了一些輔助函數,目的是爲了幫助咱們使用Vuex的時候更方便,提供了操做 store 的各類屬性的一系列語法糖。具體分爲四個輔助函數:mapState、mapMutations、mapActions和mapGetters。爲了更清晰的解釋語法糖的包裝形式,先看一下使用方法:

computed: mapState({
    // 箭頭函數可以使代碼更簡練
    count: state => state.count,

    // 傳字符串參數 'count' 等同於 `state => state.count`
    countAlias: 'count',

    // 爲了可以使用 `this` 獲取局部狀態,必須使用常規函數
    countPlusLocalState (state) {
      return state.count + this.localCount
    }
  })

對state進行舉例說明,以上代碼是使用方法,從代碼量上來看確實精簡了很多,這樣一個包裝過程當中重要的是將這個語法糖翻譯成計算機可識別的js代碼,所以在mapState的源碼封裝過程當中實際作了很重要的一件事:將state注入到vue實例的computed屬性裏,同時對於不一樣格式的語法糖進行函數形式的解構包裝便可。

同理能夠對於mapMutations、mapActions和mapGetters進行解釋:mapMutations和mapActions分別將commit 和 dispatch注入vue實例的methods裏,而mapActions是將getters注入到vue實例的computed裏,剩下的都是一些將語法糖書寫改成函數形式便可,對於這幾種的語法糖封裝方式在源碼上都大同小異。

七、plugin

vuex2.0裏提供了兩個plugin:devtool和logger。分別是接入開發者工具和輸出state變化的log插件;從源碼角度去看插件邏輯沒什麼特別說明,只是在開發插件的過程當中可能須要對於內部提供的一些api和屬性有更多的瞭解和掌握,例如subscribe,這樣咱們才能夠根據自身的需求取開發相應的插件。

八、總結

本文介紹了Vuex2.0的源碼,整個源碼量不大,經過一些簡易流程和介紹說明源碼運行機制,讓你們基本在脫離源碼的基礎上簡略理解Vuex的原理。對於一個庫和源碼的研究,研究前和研究後必定要反覆使用這個庫,閱讀前使用庫幫助咱們瞭解這是個什麼庫,能夠作什麼;閱讀後使用庫可讓咱們從更多細節上去推敲使用上的細節,以及爲何這麼使用,後者有沒有更好的使用方式。

雖然源碼讀起來有點晦澀難懂,可是當你通讀完之後能夠幫助你瞭解內部的機制,從而更好地使用Vuex,也更容易幫助你進行debug;其次經過通讀源碼,總體地理解它的設計理念以及編碼風格,幫助你往後在邁向技術高工路上進行實踐學習。

相關文章
相關標籤/搜索