• <center id="sm46c"></center>
  • <dfn id="sm46c"></dfn>
  • <strike id="sm46c"></strike>
  • <cite id="sm46c"><source id="sm46c"></source></cite>
    • <strike id="sm46c"><source id="sm46c"></source></strike>
      <option id="sm46c"></option>
      国产精品天天看天天狠,女高中生强奷系列在线播放,久久无码免费的a毛片大全,国产日韩综合av在线,亚洲国产中文综合专区在,特殊重囗味sm在线观看无码,中文字幕一区二区三区四区在线,无码任你躁久久久久久老妇蜜桃

      Vuex源碼分析

      2019-12-8    seo達人

      一、前言

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



      二、準備

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

      2)打開項目,找到examples目錄,在終端,cd進入examples,執行npm i,安裝完成之后,執行node server.js



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



      4) 得到頁面,點擊counter



      5)最終,我們就從這里出發調試吧。





      三、調試

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



      1.Vue.use()

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



      即use會調用下方的方法(debugger會進入)



      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是一個function,調用install,將 this指向插件,并將Vue作為第一個參數傳入

          // 所以調用vuex吧this指向vuex,并吧vue當參數傳入

            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文件里最下方有個install函數,會調用它



      debugger進入后



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

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

         // 如果你在開發環節,你使用了兩次Vue.use(Vuex) 就會報下方的錯誤,提醒你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) // 調用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以上 執行vue的mixin混入,該函數不懂用法請查看官方文檔,

        // mixin合并混入操作,將vuexInit 方法混入到beforeCreate生命周期,意思就是當執行beforeCreate周期的時候

        // 會執行vuexInit 方法

          Vue.mixin({ beforeCreate: vuexInit })

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

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

        // 因為該方法是在beforeCreate下執行,而beforeCreate的this指向為Vue 所以this === Vue

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

        //所以這就是我們為什么要在new Vue的時候,傳遞一個store進去的原因,

        //因為只有傳進去,才能在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是函數,執行調用options.store()

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

        // 這也就是我們為什么能夠在Vue項目中直接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,

          //從而保證子元素父元素公用一個store實例

            this.$store = options.parent.$store

          }

        }

      }





      1.3 Vue.use(Vuex)總結

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





      2.new Vuex.Store

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



      export default new Vuex.Store({

        state,

        getters,

        actions,

        mutations

      })





      debugger進入,Vuex.Store方法在src目錄下的store.js文件下



      export class Store {

        constructor (options = {}) {

        // 這里的options是在counter定義的 state,getters,actions,mutations當做參數傳進來

          // 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上的自動安裝,也就是通過script標簽引入時不需要手動調用Vue.use(Vuex)

            install(window.Vue)

          }



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

           // 開發環境 斷言,如果不符合條件 會警告,這里自行看

            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上掛載一些對象,這里暫且不要知道他們要來干什么

          // 因為是源碼分析,不要所有的代碼都清除,第一次源碼分析,你就只當他們是掛載對象,往下看

          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) //碰到第一個不是定義空對象,debugger進去,分析在下面

          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 用來監聽state getter

          resetStoreVM(this, state)



          // apply plugins

          // 插件的注冊

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

      // 下方的是開發工具方面的 暫不提

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

          if (useDevtools) {

            devtoolPlugin(this)

          }

        }

        }



      2.1 new ModuleCollection

      ModuleCollection函數在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) // 開發環境斷言,暫忽略

          }



          const newModule = new Module(rawModule, runtime)

          if (path.length === 0) { // path長度為0,為根節點,將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) { // 如果存在子模塊,遞歸調用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函數在同目錄下的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為函數,調用函數,為對象,則取對象 賦值為module上的state上

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

        }

      }



      所以Module的this內容為如下:



      2.1.3 installModule



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

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

        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)

          })

        }

      // 設置當前上下文

        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



      // 設置module的上下文,綁定對應的dispatch、commit、getters、state

      function makeLocalContext (store, namespace, path) {

        const noNamespace = namespace === ''



        const local = {

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

          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的注冊,會調用下方方法,將wrappedMutationHandler函數放入到entry中



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

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

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

        entry.push(function wrappedMutationHandler(payload) {

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

        });

      }





      2.1.6 forEachAction

      action的注冊,會調用下方方法,將wrappedActionHandler函數放入到entry中



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

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

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

        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;

          }

        });

      }



      因為entry和上面的_action[type],_mutations[type] 指向同一個地址,所以:



      2.1.7 forEachGetter

      getter的注冊,會調用下方方法



      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內容

        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設置為true,取消所有日志警告等

        Vue.config.silent = true

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

          data: {

            $$state: state

          },

          computed

        })

        // 恢復原先配置

        Vue.config.silent = silent



        // enable strict mode for new vm

        if (store.strict) {

          enableStrictMode(store)

        }

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

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

        }

      }



      看到這里,你應該對vuex有初步的了解



       // 這也是我們為什么能用訪問到state,getter的原因

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

      1

      2

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

      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)

      })



      當運行new Vue的時候,傳入了store,前面minix beforecreate,執行到beforecreate鉤子時,會調用vueInit函數,就可以在this.$store取到store對象了,因為options.store有值了 ,不為空,這樣就連貫到了,所以這就是為什么可以用this.$store取值。


      日歷

      鏈接

      個人資料

      藍藍設計的小編 http://www.li-bodun.cn

      存檔

      主站蜘蛛池模板: 亚洲最新版av无码中文字幕一区 | 极品少妇的粉嫩小泬视频| 亚洲欧洲无码av一区二区三区| 欧美日韩中文亚洲另类春色| 精品国产一区二区三区av性色| 久久国产精品久久喷水| 亚洲av色香蕉一二三区| 久久久无码精品国产一区 | 亚洲欧洲一区二区综合精品 | 国产精品成人无码免费| 国产丝袜在线精品丝袜不卡| 天码AV无码一区二区三区四区| 天干天干天啪啪夜爽爽99| 在线免费播放亚洲自拍网| 亚洲a∨国产av综合av| 久久东京热人妻无码人av| 被灌满精子的少妇视频| 欧美人做人爱a全程免费| 亚洲第一精品福利| 这里只有精品免费视频| 甘洛县| 99久久成人亚洲精品观看| 亚洲欧洲AV系列天堂日产国码| 国产免费av片在线观看麻豆| 亚洲男人天堂2018| 中文在线中文a| 91年精品国产福利线观看久久| 狠狠亚洲色一日本高清色| 成年午夜性影院| 成人免费看片又大又黄| 欧美成人一区二免费视频| 亚洲综合色婷婷| 岛国岛国免费v片在线观看| 亚洲国产精品色一区二区| 青青操国产视频| 中文字幕亚洲码在线观看| 国产亚洲精品第一综合不卡| 一本无码在线观看| 人妻av无码专区久久| 加勒比一区二区无码视频在线| 日韩av一区二区高清不卡|