JS每日一題: 請簡述一下vuex實現原理

20190221

請簡述一下vuex實現原理vue

對vuex基礎概念有不懂的能夠點 這裏

vuex實現原理咱們簡單過一遍源碼 地址 https://github.com/vuejs/vuexgit

首先咱們例出幾個問題進行思考github

  • store是怎麼註冊的?
  • mutation,commit 是怎麼實現的?
  • 輔助函數是怎麼實現的?

store是怎麼註冊的

看了下面的源碼就很清楚了, 咱們看到vuex在vue 的生命週期中的初始化鉤子前插入一段 Vuex 初始化代碼。給 Vue 的實例注入一個 $store 的屬性,這也就是爲何咱們在 Vue 的組件中能夠經過 this.$store.xxx 訪問到 Vuex 的各類數據和狀態vuex

// 源碼位置 https://github.com/vuejs/vuex/blob/665455f8daf8512e7adbf63c2842bc0b1e39efdb/src/mixin.js

export default function (Vue) {
  const version = Number(Vue.version.split('.')[0])

  if (version >= 2) {
    Vue.mixin({ beforeCreate: vuexInit })
  } else {
    // override init and inject vuex init procedure
    // for 1.x backwards compatibility.
    const _init = Vue.prototype._init
    Vue.prototype._init = function (options = {}) {
      options.init = options.init
        ? [vuexInit].concat(options.init)
        : vuexInit
      _init.call(this, options)
    }
  }

  /**
   * Vuex init hook, injected into each instances init hooks list.
   */

  function vuexInit () {
    const options = this.$options
    // store injection
    if (options.store) {
      this.$store = typeof options.store === 'function'
        ? options.store()
        : options.store
    } else if (options.parent && options.parent.$store) {
      this.$store = options.parent.$store
    }
  }
}

mutations,commit 是怎麼實現的

// 源碼位置  https://github.com/vuejs/vuex/blob/665455f8daf8512e7adbf63c2842bc0b1e39efdb/src/store.js#L417
function registerMutation (store, type, handler, path = []) {
  const entry = store._mutations[type] || (store._mutations[type] = [])
  entry.push(function wrappedMutationHandler (payload) {
    handler(getNestedState(store.state, path), payload)
  })
}

registerMutation 是對 store 的 mutation 的初始化,它接受 4 個參數,store爲當前 Store 實例,type爲 mutation 的 key,handler 爲 mutation 執行的回調函數,path 爲當前模塊的路徑。mutation 的做用就是同步修改當前模塊的 state ,函數首先經過 type 拿到對應的 mutation 對象數組, 而後把一個 mutation 的包裝函數 push 到這個數組中,這個函數接收一個參數 payload,這個就是咱們在定義 mutation 的時候接收的額外參數。這個函數執行的時候會調用 mutation 的回調函數,並經過 getNestedState(store.state, path) 方法獲得當前模塊的 state,和 playload 一塊兒做爲回調函數的參數數組

咱們知道mutation是經過commit來觸發的,這裏咱們也來看一下commit的定義app

// 源碼位置 https://github.com/vuejs/vuex/blob/665455f8daf8512e7adbf63c2842bc0b1e39efdb/src/store.js#L82

  commit (_type, _payload, _options) {
    // check object-style commit
    const {
      type,
      payload,
      options
    } = unifyObjectStyle(_type, _payload, _options)

    const mutation = { type, payload }
    const entry = this._mutations[type]
    if (!entry) {
      if (process.env.NODE_ENV !== 'production') {
        console.error(`[vuex] unknown mutation type: ${type}`)
      }
      return
    }
    this._withCommit(() => {
      entry.forEach(function commitIterator (handler) {
        handler(payload)
      })
    })
    this._subscribers.forEach(sub => sub(mutation, this.state))

    if (
      process.env.NODE_ENV !== 'production' &&
      options && options.silent
    ) {
      console.warn(
        `[vuex] mutation type: ${type}. Silent option has been removed. ` +
        'Use the filter functionality in the vue-devtools'
      )
    }
  }

commit 支持 3 個參數,type 表示 mutation 的類型,payload 表示額外的參數,根據 type 去查找對應的 mutation,若是找不到,則輸出一條錯誤信息,不然遍歷這個 type 對應的 mutation 對象數組,執行 handler(payload) 方法,這個方法就是以前定義的 wrappedMutationHandler(handler),執行它就至關於執行了 registerMutation 註冊的回調函數ide

輔助函數

輔助函數的實現都差不太多,這裏只講解mapState函數

// 源碼地址  https://github.com/vuejs/vuex/blob/665455f8daf8512e7adbf63c2842bc0b1e39efdb/src/helpers.js#L7

export const mapState = normalizeNamespace((namespace, states) => {
  const res = {}
  normalizeMap(states).forEach(({ key, val }) => {
    res[key] = function mappedState () {
      let state = this.$store.state
      let getters = this.$store.getters
      if (namespace) {
        const module = getModuleByNamespace(this.$store, 'mapState', namespace)
        if (!module) {
          return
        }
        state = module.context.state
        getters = module.context.getters
      }
      return typeof val === 'function'
        ? val.call(this, state, getters)
        : state[val]
    }
    // mark vuex getter for devtools
    res[key].vuex = true
  })
  return res
})

mapState在調用了 normalizeMap 函數後,把傳入的 states 轉換成由 {key, val} 對象構成的數組,接着調用 forEach 方法遍歷這個數組,構造一個新的對象,這個新對象每一個元素都返回一個新的函數 mappedState,函數對 val 的類型判斷,若是 val 是一個函數,則直接調用這個 val 函數,把當前 store 上的 state 和 getters 做爲參數,返回值做爲 mappedState 的返回值;不然直接把 this.$store.state[val] 做爲 mappedState 的返回值學習

爲了更直觀的理解,咱們看下最終mapState的效果this

computed: mapState({
    name: state => state.name,
  })
  
  // 等同於
  
  computed: {
    name: this.$store.state.name
  }

關於JS每日一題

JS每日一題能夠當作是一個語音答題社區
天天利用碎片時間採用60秒內的語音形式來完成當天的考題
羣主在第二天0點推送當天的參考答案

  • 注 毫不僅限於完成當天任務,更可能是查漏補缺,學習羣內其它同窗優秀的答題思路

點擊加入答題

相關文章
相關標籤/搜索