Vuex 源碼解析(如何閱讀源代碼實踐篇)

上一篇文章說的是如何閱讀框架源代碼,收到了「若是更詳細一點就行了」的反饋,不如就以 Vuex 爲切入點進行一次實踐吧,不矯揉不造做,說走咱就走~~html

1、前提

本文假定你已經對 Vue 的使用上有必定的概念,不要求輕車熟路(使用過 Vuex 固然是最好的),但至少要了解基本的事件綁定方式,以及 Mixin 的用法,官方文檔今後去前端

2、Vuex 解決了什麼問題

官方的說法:Vuex 是一個專爲 Vue.js 應用程序開發的狀態管理模式
這裏首先要搞清楚什麼是狀態,狀態就是數據,也就是說: Vuex 提供了一套 Vue 應用統一的數據源管理模式,除了定義數據源,還定義了數據的管理模式vue

這其中,Store 所包含的兩個核心部分 State 和 Actions 分別表明了數據源,和數據的管理(操做)模式,同時做爲一個全局的 VM,其有效的協調了 Vue 各組件間的通訊vuex

3、Vuex 的設計思想

若是讀 Vue 文檔的時候足夠留心,興許你能在插件一節找到蛛絲馬跡:瀏覽器

插件的功能包括,經過全局 mixin 方法添加一些組件選項,如:vuexapp

也就是說,Vuex 不過是 Vue 的一個插件,經過 Mixin 的方式給每一個組件注入一個 $store 對象,因爲每一個組件的 $store 指向的是同一個 store 對象(後面經過詳讀代碼能夠知道,這個 $store 實際上是一個 VM 對象),因此 store 是全局的,這就印證了以前在咱們爲何須要 Vuex中的一個結論,Vuex 相似於一個事件總線框架

4、詳讀代碼

經過 Mixin 注入 Store

從入口文件 index.js 開始,代碼很少,能夠直接貼出來ide

export default {
  Store,
  install,
  version: '__VERSION__',
  mapState,
  mapMutations,
  mapGetters,
  mapActions
}複製代碼

若是你一眼就看出這裏的關鍵是 install,那麼你應該領略到讀源碼先了解設計思想的獨特魅力了,沒錯,做爲 Vue 的 Plugin,install 方法就是入口函數

循着 install 方法進入 store.js,仍是符合預期,這個方法主要幹得是事情就是 mixinpost

export function install (_Vue) {
  ...
  Vue = _Vue
  applyMixin(Vue)
}

// auto install in dist mode
if (typeof window !== 'undefined' && window.Vue) {
  install(window.Vue)
}複製代碼

而且還有一個小細節,瀏覽器環境下而且 Vue 不爲空的時候,引入 Vuex 以後是會自動註冊的

具體來看看 mixin.js 這個文件,劃重點(注意看註釋):

// 經過鉤子 init / beforeCreate 執行 vuexInit
const usesInit = Vue.config._lifecycleHooks.indexOf('init') > -1
Vue.mixin(usesInit ? { init: vuexInit } : { beforeCreate: vuexInit })

// 組件初始化的時候注入 $store
function vuexInit () {
    const options = this.$options
    // store injection
    if (options.store) {
      this.$store = options.store
    } else if (options.parent && options.parent.$store) {
      this.$store = options.parent.$store
    }
}複製代碼

Store 對象

Vuex 的最佳實踐中,通常這樣使用(帶着目標去閱讀,效果更佳):

// create store
const store = new Vuex.Store({
  actions: {
    ...
  },
  modules: {
    ...
  }
})
import App from './comps/app.vue'
new Vue(Vue.util.extend({ el: '#root', store }, App))複製代碼

咱們須要新建一個 Store,在建立 Vue 實例的時候,做爲參數傳入,在上一節的 vuexInit 函數中,是從 this.$options 中取出 store 賦值給組件的 $store 的,如此,便能無縫聯繫上了

接下來的重點,就是 Store 這個類了,仍是 store.js 這個文件,懷着入參爲 ations 和 modules 的預期,來讀 constructor 方法,卻是有一個語句是用來處理 modules 的

this._modules = new ModuleCollection(options)複製代碼

但真的是尋尋覓覓尋不到從 options 中取出 actions 進行處理的方法,固然後面仔細閱讀了 ModuleCollection 中的代碼以後,才找到了答案,actions 參數也是在這裏面提取的。畢竟讓我糾結迷茫了良久,若是是我來寫的話,我可能不會這麼寫,方法的命名須要有語義性,並且一個方法也應當只作一件事情

原則上爲了儘快理清主流程,有些細節須要暫時略過(因此語義化的命名、合理的函數拆分,對閱讀者來講是多麼的重要),假設已經知道前面的步驟已經從 options 中讀到了 actions 和 modules,那麼下一個核心節點就是:

installModule(this, state, [], this._modules.root)複製代碼

這一步再進行分解(注意看註釋)

// 註冊 mutation
  module.forEachMutation((mutation, key) => {
    const namespacedType = namespace + key
    registerMutation(store, namespacedType, mutation, local)
  })

  // 註冊 action
  module.forEachAction((action, key) => {
    const namespacedType = namespace + key
    registerAction(store, namespacedType, action, local)
  })

  // 註冊 getter (computed)
  module.forEachGetter((getter, key) => {
    const namespacedType = namespace + key
    registerGetter(store, namespacedType, getter, local)
  })

  // 遍歷子模塊
  module.forEachChild((child, key) => {
    installModule(store, rootState, path.concat(key), child, hot)
  })複製代碼

出於篇幅以及但願閱讀的同窗親自實踐的目的,具體的註冊方式這裏再也不展開

進入下一個重要環節 resetStoreVM,建立 VM,實現數據監聽(注意看註釋)

function resetStoreVM (store, state, hot) {

  // bind store public getters
  // getters 其實就是 computed
  store.getters = {}
  const wrappedGetters = store._wrappedGetters
  const computed = {}
  forEachValue(wrappedGetters, (fn, key) => {
    // use computed to leverage its lazy-caching mechanism
    computed[key] = () => fn(store)
    Object.defineProperty(store.getters, key, {
      get: () => store._vm[key],
      enumerable: true // for local getters
    })
  })

  // 建立一個 Vue 實例,做爲 Store 的 VM
  store._vm = new Vue({
    data: {
      $$state: state
    },
    computed
  })
  ...
}複製代碼

5、小結

至此,Vuex 的主流程代碼基本上算是走了一遍,看似神奇,但是代碼量並不大,仍是那句話,但願閱讀的同窗可以按照這個套路本身走一遍

本文在公衆號菲麥前端同步發行:

相關文章
相關標籤/搜索