使用的版本vuex v3.5.1
在状态管理模式中state用于存储数据, actions为一系列的行为, 通过dispatch触发对应的行为进行数据变更。 而在MV*模型中数据(state)的变更会引起视图(view)的渲染更新。
实例化Store
1)在vuex实例话中主要进行一些属性的设置, 如_actions、_mutations、_wrappedGetters等
2)dispatch、commit方法设置。
3)installModule初始化module模块,在这个过程中包括mutation、actions、getter事件的注册。
4)建立响应式的数据结构
var Store = function Store (options) {
var this$1 = this;
if ( options === void 0 ) options = {};
...
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._makeLocalGettersCache = Object.create(null);
// 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 state, which is responsible for the reactivity
// (also registers _wrappedGetters as computed properties)
resetStoreState(this, state);
// apply plugins
plugins.forEach(function (plugin) { return plugin(this$1); });
var useDevtools = options.devtools !== undefined ? options.devtools : /* Vue.config.devtools */ true;
if (useDevtools) {
devtoolPlugin(this);
}
};
installModule
该过程的主要代码如下
var isRoot = !path.length;
var namespace = store._modules.getNamespace(path);
...
var local = module.context = makeLocalContext(store, namespace, path);
module.forEachMutation(function (mutation, key) {
var namespacedType = namespace + key;
registerMutation(store, namespacedType, mutation, local);
});
module.forEachAction(function (action, key) {
var type = action.root ? key : namespace + key;
var handler = action.handler || action;
registerAction(store, type, handler, local);
});
module.forEachGetter(function (getter, key) {
var namespacedType = namespace + key;
registerGetter(store, namespacedType, getter, local);
});
module.forEachChild(function (child, key) {
installModule(store, rootState, path.concat(key), child, hot);
});
有以上代码可知此方法主要进行mutation、actions、getter方法的注册,同时递归遍历子模块进行注册行为。
以mutaion代码为例,
- 通过registerMutation方法将mutation进行包装并放入_mutations对象上,。
- 在commit阶段通过type找到对应的type方法触发即可。 其他几种方法类似
function registerMutation (store, type, handler, local) {
var entry = store._mutations[type] || (store._mutations[type] = []);
entry.push(function wrappedMutationHandler (payload) {
handler.call(store, local.state, payload);
});
}
Store.prototype.commit = function commit (_type, _payload, _options) {
var this$1 = this;
// check object-style commit
var ref = unifyObjectStyle(_type, _payload, _options);
var type = ref.type;
var payload = ref.payload;
var options = ref.options;
var mutation = { type: type, payload: payload };
var entry = this._mutations[type];
...
this._withCommit(function () {
entry.forEach(function commitIterator (handler) {
handler(payload);
});
});
...
};
建立响应式数据结构
resetStoreVM进行响应式结构的创建,包括new一个Vue用于挂载数据, 将getter设置为计算属性
主要代码如下
function resetStoreVM (store, state, hot) {
var oldVm = store._vm;
// bind store public getters
store.getters = {};
// reset local getters cache
store._makeLocalGettersCache = Object.create(null);
var wrappedGetters = store._wrappedGetters;
var computed = {};
forEachValue(wrappedGetters, function (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: function () { return 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
var silent = Vue.config.silent;
Vue.config.silent = true;
store._vm = new Vue({
data: {
$$state: state
},
computed: computed
});
...
}
vuex组件中初始化
在组件的使用中可以通过store的挂载是在init方法中执行的。 代码如下
// 在beforeCreate中混入init方法
Vue.mixin({ beforeCreate: vuexInit });
...
function vuexInit () {
var options = this.$options;
// store injection
// 在根部进行挂载
// 非根部通过parent进行获取
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;
}
}
}
map封装
为了便于获取store上的数据或方法, vuex提供了map方法可批量获取。 其原理是对传入的对象进行封装,然后返回封装后的对象。 以mapMutations为例,将commit方法进行封装
methods: {
...mapMutations([
'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`
// `mapMutations` 也支持载荷:
'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)`
]),
...mapMutations({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
})
}
实现如下
var mapMutations = normalizeNamespace(function (namespace, mutations) {
var res = {};
...
normalizeMap(mutations).forEach(function (ref) {
var key = ref.key;
var val = ref.val;
res[key] = function mappedMutation () {
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
// Get the commit method from store
var commit = this.$store.commit;
if (namespace) {
var module = getModuleByNamespace(this.$store, 'mapMutations', namespace);
if (!module) {
return
}
commit = module.context.commit;
}
return typeof val === 'function'
? val.apply(this, [commit].concat(args))
: commit.apply(this.$store, [val].concat(args))
};
});
return res
});
动态加载或卸载
registerModule和unregisterModule方法,
Store.prototype.registerModule = function registerModule (path, rawModule, options) {
if ( options === void 0 ) options = {};
if (typeof path === 'string') { path = [path]; }
...
this._modules.register(path, rawModule);
installModule(this, this.state, path, this._modules.get(path), options.preserveState);
// reset store to update getters...
resetStoreVM(this, this.state);
};
Store.prototype.unregisterModule = function unregisterModule (path) {
var this$1 = this;
if (typeof path === 'string') { path = [path]; }
...
this._modules.unregister(path);
this._withCommit(function () {
var parentState = getNestedState(this$1.state, path.slice(0, -1));
Vue.delete(parentState, path[path.length - 1]);
});
resetStore(this);
};