严格模式
import { Vue } from './install'
import ModuleCollection from './module/module-collection';
import { forEach } from './util';
function getNewState(store, path) {
return path.reduce((memo, current) => {
return memo[current];
}, store.state)
}
function installModule(store, rootState, path, module) { // a/b/c/d
// 获取 moduleCollection 类的实例
let ns = store._modules.getNamespace(path);
// module.state => 放到 rootState 对应的儿子里(按层级放)
if (path.length > 0) { // 儿子模块
// 需要找到对应父模块,将状态声明上去
// {name: 'zhangsan', age: '12', a: aState}
let parent = path.slice(0, -1).reduce((memo, current) => {
return memo[current];
}, rootState);
// 对象新增属性不能导致重新更新视图
store._withCommittting(() => {
// 将新增属性变成响应式的
Vue.set(parent, path[path.length - 1], module.state);
})
}
// 遍历 getters 放入 store.wrapperGetters 中
module.forEachGetter((fn, key) => {
store.wrapperGetters[ns + key] = function () {
// 防止 this 丢失
return fn.call(store, getNewState(store, path));
}
});
// 遍历 mutation 放入 store.mutation
module.forEachMutation((fn, key) => {
// {myAge:[fn,fn]},没有命名空间的时候,要合并相同名字的 mutation
store.mutations[ns + key] = store.mutations[ns + key] || [];
store.mutations[ns + key].push((payload) => {
store._withCommittting(() => {
// 防止 this 丢失
fn.call(store, getNewState(store, path), payload); // 先调用 mutation 再执行 subscirbe
})
// 插件的更新依赖于 mutations 的执行
store._subscribes.forEach(fn => fn({ type: ns + key, payload }, store.state));
})
});
// 遍历 action 放入 store.action
module.forEachAction((fn, key) => {
store.actions[ns + key] = store.actions[ns + key] || [];
store.actions[ns + key].push((payload) => {
// 防止 this 丢失
return fn.call(store, store, payload)
})
});
// 递归安装
module.forEachChildren((child, key) => {
installModule(store, rootState, path.concat(key), child);
});
}
function resetVM(store, state) {
let oldVm = store._vm;
store.getters = {}; // 初始化 store.getters
const computed = {}; // 取 getters 属性时,把他代理到 vue 实例的计算属性 computed 上
forEach(store.wrapperGetters, (getter, key) => {
computed[key] = getter;
// 给 store.getters 赋值
Object.defineProperty(store.getters, key, {
// 在 vue 组件中使用 vuex 的 getters 时,代理到 vue 实例 store._vm 上
get: () => store._vm[key]
})
});
// 需要将 state 变成响应式的,直接将 state 放到 vue 实例的 data 里面就可以了,这样 state 就是响应式的,会自动更新视图
store._vm = new Vue({
data: {
// 以 $ 开头的数据不会被挂载到实例上,但是会挂载到当前的 _data 上,减少一次代理
// $$ 开头,表示是 vuex 自己自定义的变量
$$state: state
},
computed,
});
// 说明是严格模式要监控状态(利用 watch)
// 异步修改状态必须通过 commit mutation
if (store.strict) {
store._vm.$watch(() => store._vm._data.$$state,
() => {
// console.assert() 方法在第一个参数为 false 的情况下会在控制台输出信息
console.assert(store._committing, 'no mutate in mutation handler outside')
},
{
deep: true, // 内部会遍历所有的属性
sync: true // 状态变化,会立即执行(同步)
}
);
}
if (oldVm) { // 重新创建实例后,需要将老的实例卸载掉
Vue.nextTick(() => oldVm.$destroy())
}
}
class Store {
constructor(options) {
// 对用户的模块进行整合,整合成一棵树
// 当前格式化完毕的数据,放到了 this._modules 里
this._modules = new ModuleCollection(options); // 对用户的参数进行格式化操作
this.wrapperGetters = {};
// 需要将模块中的所有的 getters、mutations、actions 进行收集
this.mutations = {};
this.actions = {};
this._subscribes = []; // 插件集合(发布订阅模式)
this._committing = false; // 标记:是否通过 mutation 修改的属性,默认不是在 mutation 中更改的
this.strict = options.strict;
// 没有 namespace 的时候 getters 都放在根上,actions 和 mutations 会被合并数组
let state = options.state;
// 将包装后的模块进行安装
installModule(this, state, [], this._modules.root);
// getters 计算属性的实现
resetVM(this, state);
// 说明用户使用了插件
if (options.plugins) {
options.plugins.forEach(plugin => plugin(this))
}
}
// 判断修改 state 是同步还是异步修改
_withCommittting(fn) {
this._committing = true; // 如果 true
fn(); // 函数是同步的 获取_commiting 就是 true,如果是异步的那么就会变成 false 就会打印日志
this._committing = false;
}
}
// state getters action mutation (modules 分层)
export default Store;
打赏作者
您的打赏是我前进的动力
微信
支付宝
vuex 中的模块注册
上一篇
评论