1 | let moduleA = { |
以上面store结构为例,分析vuex源码
new Store
1 | var Store = function Store (options) { |
install
1 | function install (_Vue) { |
ModuleCollection
1 | var ModuleCollection = function ModuleCollection (rawRootModule) { |
1 | var Module = function Module (rawModule, runtime) { |
所以这一步结束后
1
this._modules = new ModuleCollection(options);
this._modules为如下对象
1
2
3
4
5
6
7
8
9
10ModuleCollection {
root: Module {
runtime: false,
_children: {a: Module, b: Module}
_rawModule: {state: {…}, mutations: {…}, modules: {…}}
state: {count: 0}
__proto__: Object
}
__proto__: Object
}
ModuleCollection对象上的其他方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25ModuleCollection.prototype.get = function get (path) {
return path.reduce(function (module, key) {
return module.getChild(key)
}, this.root)
};
ModuleCollection.prototype.getNamespace = function getNamespace (path) {
var module = this.root;
//对设置了namespaced的模块进行拼接
return path.reduce(function (namespace, key) {
module = module.getChild(key); //从_children取出子模块
return namespace + (module.namespaced ? key + '/' : '')
}, '')
};
ModuleCollection.prototype.update = function update$1 (rawRootModule) {
update([], this.root, rawRootModule);
};
ModuleCollection.prototype.unregister = function unregister (path) {
var parent = this.get(path.slice(0, -1));
var key = path[path.length - 1];
if (!parent.getChild(key).runtime) { return }
parent.removeChild(key);
};
installModule
1 | //初始化调用时传参 (this, state, [], this._modules.root) |
forEachValue
公共方法1
2
3function forEachValue (obj, fn) {
Object.keys(obj).forEach(function (key) { return fn(obj[key], key); });
}
注册 Mutation
1 | Module.prototype.forEachMutation = function forEachMutation (fn) { |
小结:将当前模块的mutations上的各个mutations结合namespace存储到store._mutations属性上
可见,如果子模块没有namespace,同名的mutation会跟根模块的mutation存储在相同的type下面
所以当触发commit的时候会一起执行,
如果子模块设置了namespace,这时存储的type会包含该模块对应的名称,
即使同名的mutation也被存放在不同的type中,实现了隔离
注册 Action
1 | Module.prototype.forEachAction = function forEachAction (fn) { |
小结:将当前模块的actions上的各个action结合namespace存储到store._actions属性上
注册 Getter
1 | Module.prototype.forEachGetter = function forEachGetter (fn) { |
小结:将当前模块的getters上的各个getter结合namespace存储到store._wrappedGetters属性上
注册 Module
递归调用installModule注册子模块1
2
3
4
5
6
7Module.prototype.forEachChild = function forEachChild (fn) {
forEachValue(this._children, fn);
};
module.forEachChild(function (child, key) {
installModule(store, rootState, path.concat(key), child, hot);
});
makeLocalContext
优化 dispatch, commit, getters and state
如果没有设置namespace, 就使用根模块的名称1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82 function makeLocalContext (store, namespace, path) {
var noNamespace = namespace === '';
var local = {
dispatch: noNamespace ? store.dispatch : function (_type, _payload, _options) {
var args = unifyObjectStyle(_type, _payload, _options);
var payload = args.payload;
var options = args.options;
var type = args.type;
if (!options || !options.root) {
type = namespace + type;//有namespace拼接后再触发
if (!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 : function (_type, _payload, _options) {
var args = unifyObjectStyle(_type, _payload, _options);
var payload = args.payload;
var options = args.options;
var type = args.type;
if (!options || !options.root) {
type = namespace + type; //有namespace拼接后再触发
if (!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
? function () { return store.getters; }
: function () { return makeLocalGetters(store, namespace); }
},
state: {
get: function () { return getNestedState(store.state, path); }
}
});
return local
}
function makeLocalGetters (store, namespace) {
if (!store._makeLocalGettersCache[namespace]) {
var gettersProxy = {};
var splitPos = namespace.length;
Object.keys(store.getters).forEach(function (type) {
// skip if the target getter is not match this namespace
if (type.slice(0, splitPos) !== namespace) { return }
// extract local getter type
var localType = type.slice(splitPos);
// Add a port to the getters proxy.
// Define as getter property because
// we do not want to evaluate the getters in this time.
Object.defineProperty(gettersProxy, localType, {
get: function () { return store.getters[type]; },
enumerable: true
});
});
store._makeLocalGettersCache[namespace] = gettersProxy;
}
return store._makeLocalGettersCache[namespace]
}
function getNestedState (state, path) {
return path.reduce(function (state, key) { return state[key]; }, state)
}
小结
该步骤完成后
store对象长这样
1 | commit: ƒ boundCommit(type, payload, options) |
resetStoreVM
利用vue的数据处理逻辑,解决getters和state之间订阅发布关系1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50function partial (fn, arg) {
return function () {
return fn(arg)
}
}
function resetStoreVM (store, state, hot) {
var oldVm = store._vm;
//绑定存储公共的getters
store.getters = {};
// 重置内部getters缓存到_makeLocalGettersCache
store._makeLocalGettersCache = Object.create(null);
var wrappedGetters = store._wrappedGetters;
var computed = {};
forEachValue(wrappedGetters, function (fn, key) {
//如果直接使用,闭包里面会包含oldVm
computed[key] = partial(fn, store);
Object.defineProperty(store.getters, key, {
get: function () { return store._vm[key]; },//直接调用vue的compute属性
enumerable: true // for local getters
});
});
//使用vue实例存放state和getters
var silent = Vue.config.silent;
/* Vue.config.silent暂时设置为true的目的是在new一个Vue实例的过程中不会报出一切警告 */
Vue.config.silent = true
store._vm = new Vue({
data: {
$$state: state
},
computed: computed
});
Vue.config.silent = silent;
/* 使能严格模式,保证修改store只能通过mutation */
if (store.strict) {
enableStrictMode(store);
}
if (oldVm) {
if (hot) {
// dispatch changes in all subscribed watchers
// to force getter re-evaluation for hot reloading.
store._withCommit(function () {
oldVm._data.$$state = null;
});
}
Vue.nextTick(function () { return oldVm.$destroy(); });
}
}
store上的其他方法
1 | Store.prototype.commit = function commit (_type, _payload, _options) { |