源码Exports
首先查看一下源码输出:exports
var index = {
Store: Store,
install: install,
version: '3.1.0',
mapState: mapState,
mapMutations: mapMutations,
mapGetters: mapGetters,
mapActions: mapActions,
createNamespacedHelpers: createNamespacedHelpers
};
module.exports = index;
源码可见,主要输出 Store, install, mapState, mapMutations, mapGetters, mapActions, createNamespacedHelpers ,接下来,我们对这些一一解读。
Store
- 实际项目中,我们的调用如下:
const store = new Vuex.Store({
modules: {
app,
settings,
user
},
getters
})
- 源码
var Store = function Store (options) {
var this$1 = this;
if ( options === void 0 ) options = {};
// 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) {
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.");
}
var plugins = options.plugins; if ( plugins === void 0 ) plugins = [];
var strict = options.strict; if ( strict === void 0 ) strict = false;
// store internal state
this._committing = false;
this._actions = Object.create(null);
this._actionSubscribers = [];
this._mutations = Object.create(null);
this._wrappedGetters = Object.create(null);
this._modules = new ModuleCollection(options); //构建模块树
this._modulesNamespaceMap = Object.create(null);
this._subscribers = [];
this._watcherVM = new Vue();//便于后续的监听
// bind commit and dispatch to self
var store = this;
var ref = this;
var dispatch = ref.dispatch;
var commit = ref.commit;
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)
};
// strict mode
this.strict = strict;
var state = this._modules.root.state;
// init root module.
// this also recursively registers all sub-modules
// and collects all module getters inside this._wrappedGetters
installModule(this, state, [], this._modules.root);
// initialize the store vm, which is responsible for the reactivity
// (also registers _wrappedGetters as computed properties)
resetStoreVM(this, state);
// apply plugins
plugins.forEach(function (plugin) { return plugin(this$1); });
var useDevtools = options.devtools !== undefined ? options.devtools : Vue.config.devtools;
if (useDevtools) {
devtoolPlugin(this);
}
};
- ModuleCollection 模块收集、构建模块树 Module
如上图所示,new ModuleCollection(options)的时候,主要是做register的操作。
这边的options就是Vuex.Store传染的参数
第一次register,就是设置rootModule;
最后根据循环options.modules,进行再进行一一register操作,将module的关系一一对应。
-
installModule 完成模块下的state、getters、actions、mutations的响应式初始化操作
-
resetStoreVM 初始化store._vm,利用vue的computed属性,建立getter和state的联系
-
plugins 插件
install
- 源码
function applyMixin (Vue) {
var version = Number(Vue.version.split('.')[0]);
if (version >= 2) {
Vue.mixin({ beforeCreate: vuexInit });
}else {
...
}
}
由源码可见,vue2及以上版本,是直接采用混入的方法,在beforeCreate的时候,调用vuexInit。由此衍生,vuexInit做了啥操作呢?
- vuexInit
function vuexInit () {
var 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;
}
}
由源码可见, vuexInit,主要是挂载this.$store.
mapState
mapState 根据配置将store或者getter中的对应参数返回
- 实际项目使用
// 在单独构建的版本中辅助函数为 Vuex.mapState
import { mapState } from 'vuex'
export default {
// ...
computed: mapState({
// 箭头函数可使代码更简练
count: state => state.count,
// 传字符串参数 'count' 等同于 `state => state.count`
countAlias: 'count',
// 为了能够使用 `this` 获取局部状态,必须使用常规函数
countPlusLocalState (state) {
return state.count + this.localCount
}
})
}
- 源码
var mapState = normalizeNamespace(function (namespace, states) {
var res = {};
normalizeMap(states).forEach(function (ref) {
var key = ref.key;
var val = ref.val;
res[key] = function mappedState () {
var state = this.$store.state;
var getters = this.$store.getters;
if (namespace) {
var 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
});
由源码可见,。
- normalizeNamespace 格式化Namespace
- mappedState 当 options中的 key=>value,value为fn时,将state和getters传参调用;不是的话,直接所对应的返回值。
最终返回一个res的对象,作为compute的参数 - getModuleByNamespace 根据命名空间的映射获取module。
该映射(_modulesNamespaceMap)在 installModule 的时候,已经进行了初始化。
function installModule (store, rootState, path, module, hot) {
var isRoot = !path.length;
var namespace = store._modules.getNamespace(path);
// register in namespace map
if (module.namespaced) {
store._modulesNamespaceMap[namespace] = module;
}
...
}
mapMutations、mapGetters、mapActions源码同mapState
createNamespacedHelpers
创建基于命名空间的组件绑定辅助函数。其返回一个包含 mapState、mapGetters、mapActions 和 mapMutations 的对象。它们都已经绑定在了给定的命名空间上。
- 源码
/**
* Rebinding namespace param for mapXXX function in special scoped, and return them by simple object
* @param {String} namespace
* @return {Object}
*/
var createNamespacedHelpers = function (namespace) { return ({
mapState: mapState.bind(null, namespace),
mapGetters: mapGetters.bind(null, namespace),
mapMutations: mapMutations.bind(null, namespace),
mapActions: mapActions.bind(null, namespace)
}); };
- 实际使用
import { createNamespacedHelpers } from 'vuex'
const { mapState, mapActions } = createNamespacedHelpers('some/nested/module')
export default {
computed: {
// 在 `some/nested/module` 中查找
...mapState({
a: state => state.a,
b: state => state.b
})
},
methods: {
// 在 `some/nested/module` 中查找
...mapActions([
'foo',
'bar'
])
}
}
关于数据更新的响应式
- 初始化的时候,通过vue混入的方式,将vuexInit挂载到了beforeCreated方法里面;
- vuexInit 初始化了 this.$store;
- installModule循环遍历,通过Vue.set 进行参数响应式初始化
// set state
if (!isRoot && !hot) {
var parentState = getNestedState(rootState, path.slice(0, -1));
var moduleName = path[path.length - 1];
store._withCommit(function () {
Vue.set(parentState, moduleName, module.state);
});
}
- resetStoreVM 的时候,通过computed将 state 和 getter 数据进行响应式
store.getters = {};
var wrappedGetters = store._wrappedGetters;
var computed = {};
forEachValue(wrappedGetters, function (fn, key) {
// use computed to leverage its lazy-caching mechanism
computed[key] = function () { return fn(store); };
Object.defineProperty(store.getters, key, {
get: function () { return store._vm[key]; },
enumerable: true // for local getters
});
});
var silent = Vue.config.silent;
Vue.config.silent = true;
store._vm = new Vue({
data: {
$$state: state
},
computed: computed
});
Vue.config.silent = silent;
- 而且 Vue 是在beforeCreated之后去初始化响应式数据;