Vue 源码
你想要的 vue 源码分析 · 33
数据获取 API root state 的获取是从 store.state 获取,如果有 module,那么就从 store.state.a 中获取,其中 a 是 module 的 key 获取 module 的数据本质上通过 module 名的 path 去访问到当前 module 的 state 一般获取 state 是通过 store.getters 方法,执行 store.getters,…
数据获取 API root state 的获取是从 store.state 获取,如果有 module,那么就从 store.state.a 中获取,其中 a 是 module 的 key 获取 module 的数据本质上通过 module 名的 path 去访问到当前 module 的 state 一般获取 state 是通过 store.getters 方法,执行 store.getters,…
- root state 的获取是从 store.state 获取,如果有 module,那么就从 store.state.a 中获取,其中 a 是 module 的 key
- 获取 module 的数据本质上通过 module 名的 path 去访问到当前 module 的 state
- 一般获取 state 是通过 store.getters 方法,执行 store.getters,实际上执行 rawGetter 方法,其中 rawGetter 就是在 store.getters 定义的方法
// getter的注册是在installModule阶段
function registerGetter(store, type, rawGetter, local) {
if (store._wrappedGetters[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
);
};
}
- vuex 中存储数据是对 state 的更改,vuex 显式的更改 state,只有 mutation 一种形式
- mutation 的初始化是在 installModule 阶段
- wrappedMutationHandler 关于执行定义的 mutation 函数
- mutation 必须是同步函数
function registerMutation(store, type, handler, local) {
const entry = store._mutations[type] || (store._mutations[type] = []);
entry.push(function wrappedMutationHandler(payload) {
handler.call(store, local.state, payload);
});
}
- 因为 mutation 必须是同步函数,如果需要异步更改 state,store 中有一个 actions,提供了异步更改 state
- actions 本质是提交的 mutation,mutation 再 commit,所以 actions 中可以是任何异步操作
- actions 的注册过程是在 installModule 阶段
function registerAction(store, type, handler, local) {
const entry = store._actions[type] || (store._actions[type] = []);
entry.push(function wrappedActionHandler(payload, cb) {
let res = handler.call(
store,
{
dispatch: local.dispatch,
commit: local.commit,
getters: local.getters,
state: local.state,
rootGetters: store.getters,
rootState: store.state
},
payload,
cb
);
});
}
- commit 触发的是 mutation 回调,dispatch 触发的是 action 回调
- commit 无返回值,而 dispatch 返回的是一个 Promise
export default class Store {
commit(_type, _payload, _options) {
const { type, payload, options } = unifyObjectStyle(_type, _payload, _options);
const mutation = { type, payload };
const entry = this._mutations[type];
this._withCommit(() => {
entry.forEach(function commitIterator(handler) {
handler(payload);
});
});
this._subscribers.forEach((sub) => sub(mutation, this.state));
}
dispatch(_type, _payload) {
const { type, payload } = unifyObjectStyle(_type, _payload);
const action = { type, payload };
const entry = this._actions[type];
this._actionSubscribers.forEach((sub) => sub(action, this.state));
return entry.length > 1 ? Promise.all(entry.map((handler) => handler(payload))) : entry[0](payload);
}
}
- mapGetter 访问的是$store 实例上的 getters 属性,也就是 this.$store.getters[val],在 vue 组件中可以放在计算属性中
- 每个 getters 支持 namespace,用于获取指定 module 的 getters
export const mapGetters = normalizeNamespace((namespace, getters) => {
const res = {};
normalizeMap(getters).forEach(({ key, val }) => {
val = namespace + val;
res[key] = function mappedGetter() {
return this.$store.getters[val];
};
res[key].vuex = true;
});
return res;
});
- mapMutations 支持传入额外的参数,作为提交 mutation 的 payload,最终执行的是 store.commit 方法
- 每一个 commit 对应一个 namespace,用作指定的 module 的 commit
export const mapMutations = normalizeNamespace((namespace, mutations) => {
const res = {};
normalizeMap(mutations).forEach(({ key, val }) => {
res[key] = function mappedMutation(...args) {
let commit = this.$store.commit;
if (namespace) {
const 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;
});
- mapActions 与 mapMutations 相似,知识把 commit 变成了 dispatch
- 同样也支持 namespace,用户访问指定 module 的 dispatch
export const mapActions = normalizeNamespace((namespace, actions) => {
const res = {};
normalizeMap(actions).forEach(({ key, val }) => {
res[key] = function mappedAction(...args) {
let dispatch = this.$store.dispatch;
if (namespace) {
const module = getModuleByNamespace(this.$store, "mapActions", namespace);
dispatch = module.context.dispatch;
}
return typeof val === "function" ? val.apply(this, [dispatch].concat(args)) : dispatch.apply(this.$store, [val].concat(args));
};
});
return res;
});
- 用于动态注入模块,主要是通过 resetStoreVM 重新实例化 store._vm
registerModule (path, rawModule, options = {}) {
resetStoreVM(this, this.state)
}
function resetStoreVM(store, state, hot) {
store.getters = {};
const wrappedGetters = store._wrappedGetters;
const computed = {};
// 遍历getter配置后,生成computed属性
forEachValue(wrappedGetters, (fn, key) => {
computed[key] = () => fn(store);
Object.defineProperty(store.getters, key, {
get: () => store._vm[key],
enumerable: true
});
});
const silent = Vue.config.silent;
Vue.config.silent = true;
store._vm = new Vue({
data: {
$$state: state
},
computed
});
Vue.config.silent = silent;
}
- 在 src/plugins 有两个文件 logger.js 和 devtool.js,
- logger.js 提供了一个日志打印功能,在控制台打印何时触发什么方法
- devtool.js 提供了 1、
触发vuex组件初始化的hook;2、提供“时空穿梭”:state操作的前进后退;3、mutation被执行时,提供被触发的mutation函数和当前state状态
const devtoolHook = typeof window !== "undefined" && window.__VUE_DEVTOOLS_GLOBAL_HOOK__;
export default function devtoolPlugin(store) {
// ...
// 触发vuex组件初始化的hook
devtoolHook.emit("vuex:init", store);
// 提供“时空穿梭”:state操作的前进后退
devtoolHook.on("vuex:travel-to-state", (targetState) => {
store.replaceState(targetState);
});
// mutation被执行时,提供被触发的mutation函数和当前state状态
store.subscribe((mutation, state) => {
devtoolHook.emit("vuex:mutation", mutation, state);
});
}
- this.$store.dispatch:触发 actions 的方法,返回 promise
- this.$store.commit: 触发 mutation 的方法,无返回值
- state: 存储组件状态,挂载在 vue 中的 data 中,是一个响应式对象
- getters: state 的读取方式,在读取的时候,可以修改 state
- actions: 本质是触发 commit,commit 再出触发 mutation,其中 actions 里面可以做任何异步操作
- mutation: 推荐修改 state 唯一方法
通过 mixin 的方式把 store 注入到 vue 的$store中,在组件中可以使用this.$store 访问到 store
vuex 中最巧妙的是 module 的实现,为了保证 store 的不臃肿,vuex 提出了 module 的概念,即把 store 分成若干个 module,每一个 module 可以看做是一个 store,拥有自己的 state,getters,mutation,actions,甚至子 module,并且给每一个模块根据 path 拼接一个 namespace,和设置 makelocalContext 本地上下文,保证了 module 的独立性,访问 module 的 state 时,要根据 namespace 访问
在严格模式下会有一个 watcher 去观测 state,直接修改会报警告,直接修改并没有修改 store._committing,所以判断_committing 如果为 false 则为直接修改,为 true 则说明是通过 mutation 修改
时空穿梭的原理是先提前缓存下 state change,然后使用 store.replaceState(targetState)方法,最终执行 this._vm._data.$$state = targetState,把当前 state 替换成缓存中的某个时刻的 state
- actions 不易理解,刚接触 vuex 会认为 actions 和 mutations 更改 state 的原理一致,实际上 actions 最终走的还是 mutations,这难免容易产生误导
- vuex 推荐使用 mutation 修改 state,但是除 mutation 外,在非严格模式,还可以直接绕开 mutations 修改 state,比如在 vue 组件中通过 this.$store.state.[name]更改 state,在工作中,我也发现很多人这样做
暂无评论
确认操作
请再次确认编辑图片
待上传图片 (剩余 0 张)
项目详情
确认评论邮箱
邀请制评论