<strike id="cy2gs"><menu id="cy2gs"></menu></strike>
  • <del id="cy2gs"><dfn id="cy2gs"></dfn></del>
  • Vuex源碼分析

    2019-12-8    seo達(dá)人

    一、前言

    我們都知道,vue組件中通信是用props進(jìn)行父子通信,或者用provide和inject注入的方法,后者類似與redux的porvider,父組件使用,包含在里面的子組件都可以使用,provide/inject用法看我的博客(provide/inject用法),provide/indect官方文檔,不過provide/indect一般用的不多,都是用前者,但是props有一個(gè)問題,父傳子沒問題,但是子后面還有子,子后面還有子,子子孫孫無窮盡也,所以這就要一層層的傳下去,太麻煩了,所以vuex就派上用場了,vuex作為一個(gè)很輕量的狀態(tài)管理器,有著簡單易用的的API接口,在任意組件里面都能使用,獲取全局狀態(tài),統(tǒng)一獲取改變,今天,就看一下源碼怎么實(shí)現(xiàn)的。



    二、準(zhǔn)備

    1)先去github上將vuex源碼下載下來。

    2)打開項(xiàng)目,找到examples目錄,在終端,cd進(jìn)入examples,執(zhí)行npm i,安裝完成之后,執(zhí)行node server.js



    3)執(zhí)行上述之后,會(huì)得到下方的結(jié)果,表示編譯完成,打開瀏覽器 輸入 localhost:8080



    4) 得到頁面,點(diǎn)擊counter



    5)最終,我們就從這里出發(fā)調(diào)試吧。





    三、調(diào)試

    找到counter目錄下的store.js 寫一個(gè)debugger,瀏覽器打開F12,刷新頁面,調(diào)試開始。



    1.Vue.use()

    先進(jìn)入Vue.use(Vuex),這是Vue使用插件時(shí)的用法,官方文檔也說了,Vue.use,會(huì)調(diào)用插件的install方法,所以如果你要寫插件的話,你就要暴露一個(gè)install方法,詳情請看vue官方文檔之use的用法



    即use會(huì)調(diào)用下方的方法(debugger會(huì)進(jìn)入)



    function initUse (Vue) {

      Vue.use = function (plugin) {

        var installedPlugins = (this._installedPlugins || (this._installedPlugins = []));

        if (installedPlugins.indexOf(plugin) > -1) {

          return this

        }



        // additional parameters

        var args = toArray(arguments, 1);

        args.unshift(this);

        if (typeof plugin.install === 'function') { 

        // 如果插件的install是一個(gè)function,調(diào)用install,將 this指向插件,并將Vue作為第一個(gè)參數(shù)傳入

        // 所以調(diào)用vuex吧this指向vuex,并吧vue當(dāng)參數(shù)傳入

          plugin.install.apply(plugin, args);

        } else if (typeof plugin === 'function') {

          plugin.apply(null, args);

        }

        installedPlugins.push(plugin);

        return this

      };

    }





    1.1 vuex的install方法

    在源碼目錄的src目錄下的store.js文件里最下方有個(gè)install函數(shù),會(huì)調(diào)用它



    debugger進(jìn)入后



    export function install (_Vue) { // _Vue是上面說的Vue作為第一個(gè)參數(shù) ,指針this指向Vuex

      if (Vue && _Vue === Vue) {

       // 如果你在開發(fā)環(huán)節(jié),你使用了兩次Vue.use(Vuex) 就會(huì)報(bào)下方的錯(cuò)誤,提醒你vue只能被use一次,可以自行試試

        if (process.env.NODE_ENV !== 'production') {

          console.error(

            '[vuex] already installed. Vue.use(Vuex) should be called only once.'

          )

        }

        return

      }

      Vue = _Vue

      applyMixin(Vue) // 調(diào)用applyMixin方法

    }



    1.2 vuex的applyMixin方法

    applyMixin方法在mixin.js文件里 同樣在src目錄下



    export default function (Vue) {

      const version = Number(Vue.version.split('.')[0]) // 獲取vue的版本



      if (version >= 2) { // vue的版本是2.xx以上 執(zhí)行vue的mixin混入,該函數(shù)不懂用法請查看官方文檔,

      // mixin合并混入操作,將vuexInit 方法混入到beforeCreate生命周期,意思就是當(dāng)執(zhí)行beforeCreate周期的時(shí)候

      // 會(huì)執(zhí)行vuexInit 方法

        Vue.mixin({ beforeCreate: vuexInit })

      } else { // 1.xxx版本太老了 現(xiàn)在基本不用,暫不講解,可以自行了解

        // 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 () { 

      // 因?yàn)樵摲椒ㄊ窃赽eforeCreate下執(zhí)行,而beforeCreate的this指向?yàn)閂ue 所以this === Vue

      // 獲得vue里面的option配置,這里涉及到vue的源碼,以后再講vue ,

      //所以這就是我們?yōu)槭裁匆趎ew Vue的時(shí)候,傳遞一個(gè)store進(jìn)去的原因,

      //因?yàn)橹挥袀鬟M(jìn)去,才能在options中獲取到store

      /


      var vm = new Vue({

    el: "#app",

    data() {return{}},

    store

    })

    */

        const options = this.$options

        // store injection

        if (options.store) { 

        // 如果options對象中store有,代表是root ,如果options.store是函數(shù),執(zhí)行調(diào)用options.store()

        // 如果是對象直接options.store 賦值給this.$stroe

      // 這也就是我們?yōu)槭裁茨軌蛟赩ue項(xiàng)目中直接this.$store.disptach('xxx')的原因,從這里獲取

          this.$store = typeof options.store === 'function'

            ? options.store()

            : options.store

        } else if (options.parent && options.parent.$store) { 

        // 如果options.store為空,則判斷options.parent.$store有沒有 從父元素判斷有沒有store,

        //從而保證子元素父元素公用一個(gè)store實(shí)例

          this.$store = options.parent.$store

        }

      }

    }





    1.3 Vue.use(Vuex)總結(jié)

    至此,Vue.use(Vuex)全部分析完成,總結(jié),就是Vue.use調(diào)用Vuex的install的方法,然后install使用mixin混入beforecreate生命周期中混入一個(gè)函數(shù),當(dāng)執(zhí)行生命周期beforecreate的時(shí)候回執(zhí)行vuexInit 。你可以慢慢調(diào)試,所以要好好利用下方的調(diào)試按鈕,第二個(gè)按鈕執(zhí)行下一步,第三個(gè)進(jìn)入方法,兩個(gè)配合使用。





    2.new Vuex.Store

    Vue.use(Vuex)已經(jīng)結(jié)束,再回到counter目錄下的store.js文件



    export default new Vuex.Store({

      state,

      getters,

      actions,

      mutations

    })





    debugger進(jìn)入,Vuex.Store方法在src目錄下的store.js文件下



    export class Store {

      constructor (options = {}) {

      // 這里的options是在counter定義的 state,getters,actions,mutations當(dāng)做參數(shù)傳進(jìn)來

        // Auto install if it is not done yet and window has Vue.

        // To allow users to avoid auto-installation in some cases,

        // this code should be placed here. See #731

        if (!Vue && typeof window !== 'undefined' && window.Vue) {

        // 掛載在window上的自動(dòng)安裝,也就是通過script標(biāo)簽引入時(shí)不需要手動(dòng)調(diào)用Vue.use(Vuex)

          install(window.Vue)

        }



        if (process.env.NODE_ENV !== 'production') { 

         // 開發(fā)環(huán)境 斷言,如果不符合條件 會(huì)警告,這里自行看

          assert(Vue, must call Vue.use(Vuex) before creating a store instance.)

          assert(typeof Promise !== 'undefined', vuex requires a Promise polyfill in this browser.)

          assert(this instanceof Store, store must be called with the new operator.)

        }



        const {

          plugins = [],

          strict = false

        } = options



        // store internal state

        //下方是在Vuex的this上掛載一些對象,這里暫且不要知道他們要來干什么

        // 因?yàn)槭窃创a分析,不要所有的代碼都清除,第一次源碼分析,你就只當(dāng)他們是掛載對象,往下看

        this._committing = false

        this._actions = Object.create(null)

        this._actionSubscribers = []

        this._mutations = Object.create(null)

        this._wrappedGetters = Object.create(null)

        // ModuleCollection代表模塊收集,形成模塊樹

        this._modules = new ModuleCollection(options) //碰到第一個(gè)不是定義空對象,debugger進(jìn)去,分析在下面

        this._modulesNamespaceMap = Object.create(null)

        this._subscribers = []

        this._watcherVM = new Vue()

        this._makeLocalGettersCache = Object.create(null)



        // bind commit and dispatch to self

        const store = this

        const { dispatch, commit } = this

        this.dispatch = function boundDispatch (type, payload) { // 綁定dispatch的指針為store

          return dispatch.call(store, type, payload)

        }

        this.commit = function boundCommit (type, payload, options) { // 綁定commit的指針為store

          return commit.call(store, type, payload, options)

        }



        // strict mode

        this.strict = strict



        const state = this._modules.root.state // 獲取到用戶定義的state



        // init root module.

        // this also recursively registers all sub-modules

        // and collects all module getters inside this._wrappedGetters

        // 初始化root模塊 注冊getters,actions,mutations 子模塊

        installModule(this, state, [], this._modules.root)



        // initialize the store vm, which is responsible for the reactivity

        // (also registers _wrappedGetters as computed properties)

        // 初始化vm 用來監(jiān)聽state getter

        resetStoreVM(this, state)



        // apply plugins

        // 插件的注冊

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

    // 下方的是開發(fā)工具方面的 暫不提

        const useDevtools = options.devtools !== undefined ? options.devtools : Vue.config.devtools

        if (useDevtools) {

          devtoolPlugin(this)

        }

      }

      }



    2.1 new ModuleCollection

    ModuleCollection函數(shù)在src目錄下的module目錄下的module-collection.js文件下



    export default class ModuleCollection {

      constructor (rawRootModule) { // rawRootModule === options

        // register root module (Vuex.Store options)

        this.register([], rawRootModule, false)

      }

    }



    2.1.1 register()



     register (path, rawModule, runtime = true) {

     // register([],options,false)

        if (process.env.NODE_ENV !== 'production') {

          assertRawModule(path, rawModule) // 開發(fā)環(huán)境斷言,暫忽略

        }



        const newModule = new Module(rawModule, runtime)

        if (path.length === 0) { // path長度為0,為根節(jié)點(diǎn),將newModule 賦值為root

          this.root = newModule

        } else {

          const parent = this.get(path.slice(0, -1))

          parent.addChild(path[path.length - 1], newModule)

        }



        // register nested modules

        if (rawModule.modules) { // 如果存在子模塊,遞歸調(diào)用register,形成一棵樹

          forEachValue(rawModule.modules, (rawChildModule, key) => {

            this.register(path.concat(key), rawChildModule, runtime)

          })

        }

      }

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    2.1.2 new Module()

    Module函數(shù)在同目錄下的modele.js文件下



    export default class Module {

    // new Module(options,false)

      constructor (rawModule, runtime) {

        this.runtime = runtime

        // Store some children item

        this._children = Object.create(null)

        // Store the origin module object which passed by programmer

        this._rawModule = rawModule // 將options放到Module上 用_raModele上

        const rawState = rawModule.state // 將你定義的state取出



        // Store the origin module's state

        // 如果你定義的state為函數(shù),調(diào)用函數(shù),為對象,則取對象 賦值為module上的state上

        this.state = (typeof rawState === 'function' ? rawState() : rawState) || {}

      }

    }



    所以Module的this內(nèi)容為如下:



    2.1.3 installModule



    function installModule (store, rootState, path, module, hot) {

    // installModule(Vuex,state,[],module) module是前面 new ModuleCollection產(chǎn)生的對象

      const isRoot = !path.length

      const namespace = store._modules.getNamespace(path)



      // register in namespace map

      if (module.namespaced) {

        if (store._modulesNamespaceMap[namespace] && process.env.NODE_ENV !== 'production') {

          console.error([vuex] duplicate namespace ${namespace} for the namespaced module ${path.join('/')})

        }

        store._modulesNamespaceMap[namespace] = module

      }



      // set state

      if (!isRoot && !hot) {

        const parentState = getNestedState(rootState, path.slice(0, -1))

        const moduleName = path[path.length - 1]

        store._withCommit(() => {

          if (process.env.NODE_ENV !== 'production') {

            if (moduleName in parentState) {

              console.warn(

                [vuex] state field "${moduleName}" was overridden by a module with the same name at "${path.join('.')}"

              )

            }

          }

          Vue.set(parentState, moduleName, module.state)

        })

      }

    // 設(shè)置當(dāng)前上下文

      const local = module.context = makeLocalContext(store, namespace, path)

    // 注冊mutation

      module.forEachMutation((mutation, key) => {

        const namespacedType = namespace + key

        registerMutation(store, namespacedType, mutation, local)

      })

    // 注冊action

      module.forEachAction((action, key) => {

        const type = action.root ? key : namespace + key

        const handler = action.handler || action

        registerAction(store, type, handler, local)

      })

    // 注冊getter

      module.forEachGetter((getter, key) => {

        const namespacedType = namespace + key

        registerGetter(store, namespacedType, getter, local)

      })

    // 逐一注冊子module

      module.forEachChild((child, key) => {

        installModule(store, rootState, path.concat(key), child, hot)

      })

    }



    2.1.4 makeLocalContext



    // 設(shè)置module的上下文,綁定對應(yīng)的dispatch、commit、getters、state

    function makeLocalContext (store, namespace, path) {

      const noNamespace = namespace === ''



      const local = {

       //noNamespace 為true 使用原先的 至于后面的 不知道干啥用的 后面繼續(xù)研究

        dispatch: noNamespace ? store.dispatch : (_type, _payload, _options) => {

          const args = unifyObjectStyle(_type, _payload, _options)

          const { payload, options } = args

          let { type } = args



          if (!options || !options.root) {

            type = namespace + type

            if (process.env.NODE_ENV !== 'production' && !store._actions[type]) {

              console.error([vuex] unknown local action type: ${args.type}, global type: ${type})

              return

            }

          }



          return store.dispatch(type, payload)

        },



        commit: noNamespace ? store.commit : (_type, _payload, _options) => {

        //noNamespace 為true 使用原先的

          const args = unifyObjectStyle(_type, _payload, _options)

          const { payload, options } = args

          let { type } = args



          if (!options || !options.root) {

            type = namespace + type

            if (process.env.NODE_ENV !== 'production' && !store._mutations[type]) {

              console.error([vuex] unknown local mutation type: ${args.type}, global type: ${type})

              return

            }

          }



          store.commit(type, payload, options)

        }

      }



      // getters and state object must be gotten lazily

      // because they will be changed by vm update

      Object.defineProperties(local, {

        getters: {

          get: noNamespace

            ? () => store.getters

            : () => makeLocalGetters(store, namespace)

        },

        state: {

          get: () => getNestedState(store.state, path)

        }

      })



      return local

    }



    function getNestedState (state, path) {

      return path.reduce((state, key) => state[key], state)

    }

    2.1.5 registerMutation

    mutation的注冊,會(huì)調(diào)用下方方法,將wrappedMutationHandler函數(shù)放入到entry中



    function registerMutation(store, type, handler, local) {

     // entry和store._mutations[type] 指向同一個(gè)地址

      var entry = store._mutations[type] || (store._mutations[type] = []);

      entry.push(function wrappedMutationHandler(payload) {

        handler.call(store, local.state, payload);

      });

    }





    2.1.6 forEachAction

    action的注冊,會(huì)調(diào)用下方方法,將wrappedActionHandler函數(shù)放入到entry中



    function registerAction(store, type, handler, local) {

      var entry = store._actions[type] || (store._actions[type] = []);

       // entry和store._actions[type]指向同一個(gè)地址

      entry.push(function wrappedActionHandler(payload) {

        var res = handler.call(store, {

          dispatch: local.dispatch,

          commit: local.commit,

          getters: local.getters,

          state: local.state,

          rootGetters: store.getters,

          rootState: store.state

        }, payload);

        if (!(0, _util.isPromise)(res)) {

          res = Promise.resolve(res);

        }

        if (store._devtoolHook) {

          return res.catch(function (err) {

            store._devtoolHook.emit('vuex:error', err);

            throw err;

          });

        } else {

          return res;

        }

      });

    }



    因?yàn)閑ntry和上面的_action[type],_mutations[type] 指向同一個(gè)地址,所以:



    2.1.7 forEachGetter

    getter的注冊,會(huì)調(diào)用下方方法



    function registerGetter(store, type, rawGetter, local) {

      if (store._wrappedGetters[type]) {

        if (true) {

          console.error('[vuex] duplicate getter key: ' + type);

        }

        return;

      }

      store._wrappedGetters[type] = function wrappedGetter(store) {

        return rawGetter(local.state, // local state

        local.getters, // local getters

        store.state, // root state

        store.getters // root getters

        );

      };

    }







    2.1.8 resetStoreVM



    function resetStoreVM (store, state, hot) {

      const oldVm = store._vm //將_vm用變量保存



      // bind store public getters

      store.getters = {}

      // reset local getters cache

      store._makeLocalGettersCache = Object.create(null)

      const wrappedGetters = store._wrappedGetters // 獲取installModule方法完成的_wrappedGetters內(nèi)容

      const computed = {}

      forEachValue(wrappedGetters, (fn, key) => {

        // use computed to leverage its lazy-caching mechanism

        // direct inline function use will lead to closure preserving oldVm.

        // using partial to return function with only arguments preserved in closure environment.

        computed[key] = partial(fn, store)

        Object.defineProperty(store.getters, key, {

        // 攔截get返回store._vm[key]中的值,即可以通過 this.$store.getters.xxx訪問值

          get: () => store._vm[key],

          enumerable: true // for local getters

        })

      })



      // use a Vue instance to store the state tree

      // suppress warnings just in case the user has added

      // some funky global mixins

      const silent = Vue.config.silent

      // silent設(shè)置為true,取消所有日志警告等

      Vue.config.silent = true

      store._vm = new Vue({ // 將state,getter的值進(jìn)行監(jiān)聽,從這里就可以看出getter其實(shí)就是采用的vue的computed

        data: {

          $$state: state

        },

        computed

      })

      // 恢復(fù)原先配置

      Vue.config.silent = silent



      // enable strict mode for new vm

      if (store.strict) {

        enableStrictMode(store)

      }

    // 若存在舊的實(shí)例,解除對state的引用,等dom更新后把舊的vue實(shí)例銷毀

      if (oldVm) {

        if (hot) {

          // dispatch changes in all subscribed watchers

          // to force getter re-evaluation for hot reloading.

          store._withCommit(() => {

            oldVm._data.$$state = null

          })

        }

        Vue.nextTick(() => oldVm.$destroy())

      }

    }



    看到這里,你應(yīng)該對vuex有初步的了解



     // 這也是我們?yōu)槭裁茨苡迷L問到state,getter的原因

     //this.store.dispatch('xxx') ,this.$store.dispatch('xxx')

    1

    2

    相信你也有點(diǎn)亂,其實(shí)上面很多我沒講到的不是我不想講,是具體我也不知道干啥的,看源碼學(xué)習(xí)呢,看主要就行,后面理解多了,其他的就慢慢都會(huì),你不可能剛開始看,就每一行,他寫的每一句的用途都知道是干什么的,只能先看主要方法,在慢慢琢磨,一步步來吧,別急,現(xiàn)在我來畫一個(gè)流程圖,更好的去理解吧。

    2.1.9 流程圖





    3.連貫

    import Vue from 'vue'

    import Counter from './Counter.vue'

    import store from './store'



    new Vue({

      el: '#app',

      store,

      render: h => h(Counter)

    })



    當(dāng)運(yùn)行new Vue的時(shí)候,傳入了store,前面minix beforecreate,執(zhí)行到beforecreate鉤子時(shí),會(huì)調(diào)用vueInit函數(shù),就可以在this.$store取到store對象了,因?yàn)閛ptions.store有值了 ,不為空,這樣就連貫到了,所以這就是為什么可以用this.$store取值。


    日歷

    鏈接

    個(gè)人資料

    存檔

    主站蜘蛛池模板: 国模精品一区二区三区| 无码国内精品久久人妻麻豆按摩 | 亚洲精品中文字幕乱码三区| 亚洲精品国产成人99久久| 精品国产乱码久久久久久1区2区 | 亚洲日韩中文在线精品第一| 911亚洲精品国产自产| 精品亚洲成a人片在线观看少妇| 欧美日韩国产中文精品字幕自在自线 | 久久久久亚洲精品天堂久久久久久 | 99亚洲精品视频| 国产精品亚洲午夜一区二区三区| 四虎国产精品永久在线观看| 亚洲av无码成人精品区| 久久久久久亚洲精品不卡| 国产系列高清精品第一页| 一本久久a久久精品综合夜夜| 国产精品成人观看视频免费| 精品无码人妻一区二区三区品 | 国产精品爽爽ⅴa在线观看| 青青青国产依人精品视频| 国产高清在线精品一区| 国精品无码一区二区三区在线| 欧洲精品99毛片免费高清观看| 亚洲精品老司机在线观看| 日韩视频中文字幕精品偷拍| 久久精品亚洲男人的天堂| 精品水蜜桃久久久久久久| 国产精品无码永久免费888| 99热这里只有精品国产66| 国产精品美女久久久久av爽| 国产精品亚洲欧美大片在线观看 | 国产在线精品国自产拍影院| 国产欧美一区二区精品性色99| 国产精品亚洲欧美大片在线看 | 久久精品国产黑森林| 日本五区在线不卡精品| 亚洲精品A在线观看| 一本色道久久88—综合亚洲精品| 亚洲午夜精品久久久久久浪潮| 亚洲精品国产精品乱码不卡√|