<strike id="g3zqm"></strike>

      <cite id="g3zqm"></cite>

        <tr id="g3zqm"><center id="g3zqm"></center></tr>
        <pre id="g3zqm"><sup id="g3zqm"></sup></pre>
        <li id="g3zqm"></li>
      1. 少妇高潮激情一区二区三,免费av深夜在线观看,亚洲狼人久久伊人久久伊,久久精品人人做人人爽电影蜜月,黄色特级片一区二区三区,欧美日韩在线亚洲二区综二,极品少妇无套内射视频,日本极品少妇videossexhd

        Vuex源碼分析

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

        一、前言

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



        二、準(zhǔn)備

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

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



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



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



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





        三、調(diào)試

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



        1.Vue.use()

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



        即use會調(diào)用下方的方法(debugger會進(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是一個function,調(diào)用install,將 this指向插件,并將Vue作為第一個參數(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文件里最下方有個install函數(shù),會調(diào)用它



        debugger進(jìn)入后



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

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

           // 如果你在開發(fā)環(huán)節(jié),你使用了兩次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) // 調(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周期的時候

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

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

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

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

          //因為只有傳進(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項目中直接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)總結(jié)

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





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

              install(window.Vue)

            }



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

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

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

            // 因為是源碼分析,不要所有的代碼都清除,第一次源碼分析,你就只當(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) //碰到第一個不是定義空對象,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的注冊,會調(diào)用下方方法,將wrappedMutationHandler函數(shù)放入到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的注冊,會調(diào)用下方方法,將wrappedActionHandler函數(shù)放入到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的注冊,會調(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其實就是采用的vue的computed

            data: {

              $$state: state

            },

            computed

          })

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

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

          }

        }



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



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

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

        1

        2

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

        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的時候,傳入了store,前面minix beforecreate,執(zhí)行到beforecreate鉤子時,會調(diào)用vueInit函數(shù),就可以在this.$store取到store對象了,因為options.store有值了 ,不為空,這樣就連貫到了,所以這就是為什么可以用this.$store取值。


        日歷

        鏈接

        個人資料

        藍(lán)藍(lán)設(shè)計的小編 http://m.hengshangtqd.cn

        存檔

        主站蜘蛛池模板: 国产精品99一区不卡| 天天躁狠狠躁狠狠躁夜夜躁| 亚洲成人A√| 中字幕人妻一区二区三区| 国产成人久久婷婷精品流白浆| 亚洲Va中文字幕久久无码一区| 亚洲va久久久噜噜噜久久狠狠| 中文字幕熟女| 成人精品视频在线观看播放| 日夜啪啪一区二区三区| 日日夜夜撸撸| 人妻无码TⅤ| 国产精品老熟女露脸视频| 丁香五月网久久综合| 亚洲大尺度一区二区av| 东京热?影音资源先锋| 久久无码喷吹高潮播放不卡| 人妻综合第一页| 久热这里有精品视频播放| 亚洲另类图| 日日躁亚洲精品| 爆乳中文字幕一区| 亚洲成AV人的天堂在线观看| 国产精品V在线播放| 午夜福利国产精品视频| 人妻系列无码专区免费| 国模小黎自慰337P人体| 色吊丝亚洲欧美| 麻豆文化传媒精品一区二区| 精品无码一区二区三区爱欲小说| 国产精品久久久久无码网站| 亚洲成a人无码av波多野| 91网站免费在线观看| 精品一区二区三区在线观看| 91最新精品丝袜国产在线| 亚洲欧美精品综合在线观看| 久久精品中文字幕有码| 无码熟妇人妻av影音先锋| 国产美女被遭强高潮免费一视频| 国产精品免费看久久久青青| 亚洲欧洲成人精品久久一码二码|