zoukankan      html  css  js  c++  java
  • Vuex源码阅读(一) new Vuex.Store()内部探究

    1. 前言

    Vuex版本:3.4.0

    Vuex仓库:https://github.com/vuejs/vuex

    Vux文档:https://vuex.vuejs.org/zh/guide/

    文章时间:2020-06-09

    2. 执行顺序

    首先看个简单的代码块:

    // store.js
    import Vue from 'vue';
    import Vuex from 'vuex';
    Vue.use(Vuex);
    
    let baseStore = new Vuex.Store({
        state: {
            count: 0
        },
    });
    export default baseStore;
    
    // app.js
    import Vue from 'vue'
    import store from './store'
    
    new Vue({
        el: '#app',
        store
    })
    

    2.1 第一步:Vue.use(Vuex)

    说明:这一步是Vuex在Vue的beforeCreate事件内增加一个回调函数,其目的是为把初始化后的store对象挂载到this.$store,即Vue.$store。

    代码

    Vue.mixin({ beforeCreate: vuexInit });
    
    function vuexInit() {
        const options = this.$options;
        // store injection store注入
        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;
        }
    }
    

      

    2.2 第二步:new Vuex.Store({})

    说明:初始化具体的store对象。

    2.3 第三步:new Vue({ store })

    说明:这里主要是为了执行第一步的代码,因为第一步的Vue.use(Vuex)只是注入一个回调,内部的条件判断options.store 和 options.parent && options.parent.$store都没有生效,只有在这一步时才会生效,其目的就向上面说的把初始化后的store对象挂载到this.$store,这样所有子组件就可以通过this.$store调用store对象。

    代码

    new Vue({
    	el: '#app',
    	router,
    	components: { App },
    	store: {
    		baseStore: baseStore
    	},
    	template: '<App/>'
    });

    3. 探究new Vuex.Store({})

    说明:这里将着重介绍new Vuex.Store({})都干了什么。

    注意:此处的讲解都是以使用单一状态树为前提条件,没有Module以及Module内的namespaced等知识点,modules这块会单独讲解。

    3.1 重新绑定dispatch、commit

    说明:此处重新绑定dispatch、commit方法,把store自身插入到第一个参数前面。

    这也是为什么我们在外部调用this.$store.dispatch('actionName')时,所创建的action第一个参数为store本身。

    代码

    this.dispatch = function boundDispatch (type, payload) {
        return dispatch.call(store, type, payload)
    }
    this.commit = function boundCommit (type, payload, options) {
        return commit.call(store, type, payload, options)
    }
    

    3.2 转换为Module对象

    说明:在这里Vuex将传入的Vuex代码解析为Model对象(后面将以options表示传入的代码块):

    new Vuex.Store({
        state: {
            count: 0
        },
        getters,
        actions,
        mutations
    });
    

    在Vuex源码中,会将options转换为Module集合:

    代码

    // src/store.js

    this._modules = new ModuleCollection(options)

    其this._modules,即Model对象初始化后的字段含义为:

    root: { // Module对象
        state:{
        count: 0
      } // 传入Vuex({options})内的state
        _children: {} // 内部嵌套Module
        _rawModule: options // 传入的options对象
    }

    3.3 installModule

    说明:在这里将对Module进行封装处理。

    处理步骤

    1) 若module.namespaced = true : 此Module将被加入store._modulesNamespaceMap内,其key为Module嵌套的路径。

    if (module.namespaced) {
        store._modulesNamespaceMap[namespace] = module
    }

    2) 非root Module时:子Module.state注入到父节点的state对象里。

     if (!isRoot && !hot) {
        const parentState = getNestedState(rootState, path.slice(0, -1)) // path.slice(0, -1) 表示只返回前面的父节点
        const moduleName = path[path.length - 1]
        store._withCommit(() => {
            Vue.set(parentState, moduleName, module.state)
        })
    }
    

    3)  对store进行局部化,这里主要对module.namespaced= true 的module进行另外处理,其内部的成员都需要进行namespace路径处理处理。

    官方说明:默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的——这样使得多个模块能够对同一 mutation 或 action 作出响应。 如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。

    4) 对module的mutation进行封装:

      ①添加到store._mutations数组内,_mutations的key默认为mutation的名称,如果module.namespaced = true,那么key就为namespace+mutation的名称。可多个module注册同名。

      ②将module.mutation第二个参数修改为局部化的state。

    const entry = store._mutations[type] || (store._mutations[type] = []);
    entry.push(function wrappedMutationHandler(payload) {
        handler.call(store, local.state, payload);
    });            

    5) 对module的action进行封装:

      ①添加到store._actions数组内,_actions的key默认为action的名称,如果module.namespaced = true,那么key就为namespace+action的名称。可多个module注册同名。

      ②将module.action第二个参数修改为局部化和root的state。

    onst entry = store._actions[type] || (store._actions[type] = []);
    entry.push(function wrappedActionHandler(payload) {
    	let res = handler.call(
    		store,
    		{
    			dispatch: local.dispatch,
    			commit: local.commit,
    			getters: local.getters,
    			state: local.state,
    			rootGetters: store.getters,
    			rootState: store.state
    		},
    		payload
    	);
    	if (!isPromise(res)) {
    		res = Promise.resolve(res);
    	}
    	if (store._devtoolHook) {
    		return res.catch((err) => {
    			store._devtoolHook.emit('vuex:error', err);
    			throw err;
    		});
    	} else {
    		return res;
    	}
    });

    6) 对module的getter进行封装:

      ①添加到store._wrappedGetters数组内,_wrappedGetters的key默认为action的名称,如果module.namespaced = true,那么key就为namespace+action的名称。只能单一注册。

      ②module.mutation第一个参数修改为局部化和root的state。

    if (store._wrappedGetters[type]) {
    	if (__DEV__) {
    		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
    	);
    };

    7) 若当前module含有子module时,遍历当前model的_children属性,迭代执行installModule。

    3.4 resetStoreVM

    说明:初始化storevm,并负责将getter注册为计算属性,并保存缓存特性。

    处理步骤:

    1) 遍历wrappedGetters,封装到store.getters里。

    forEachValue(wrappedGetters, (fn, key) => {
        // 使用computed来利用其延迟缓存机制
        // 直接内联函数的使用将导致保留oldVm的闭包。
        // 使用partial返回只保留闭包环境中的参数的函数。
        // 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],
          enumerable: true // for local getters
        })
    })
    

    2) 初始化store._vm,传入data和computed。

    说明:这里主要把state与getter(computed)进行绑定,state有更改时,getter(computed)进行自动更新,采用的方式就是本地创建一个Vue对象。

    store._vm = new Vue({
        data: {
            $$state: state
        },
        computed
    })
    

    3.5 注册插件

    4. commit()执行了什么

    当在action内调用了commit方法时,其内部步骤如下:

    1) 从store._mutations[]数组内读取对应的mutation名称的数组。

    2) 从获取到的_mutations[]数组遍历执行对应的mutation处理程序。

    3) 触发store._subscribers对应的回调。

    5. dispatch()执行了什么

    在通过this.$store.dispatch()调用对应的action时,其内部步骤如下:

    1) 与commit()调用的方法类似,从store._actions[]数组内获取对应的acticon数组。

    2) 执行active对应的处理程序并以Promise返回。

  • 相关阅读:
    计算器部分代码
    学写压缩壳心得系列之二 掌握PE结构 ,曲径通幽
    headerfiles
    VC实现文件拖拽
    学写压缩壳心得系列之三 模拟加载,步步为营
    ASPack 2.x (without poly) > Alexey Solodovnikov [Overlay]脱壳
    学写压缩壳心得系列之一 熟悉概念,未雨绸缪
    upx最新壳脱壳测试
    正则表达式大全
    win7 iis http 500 错误
  • 原文地址:https://www.cnblogs.com/polk6/p/13065289.html
Copyright © 2011-2022 走看看