Vuex
是官方提供的状态管理库,为了深入了解它的实现逻辑,本文我们一起来一步步实现它。
// 1. 引入createStore函数
import { createStore } from "vuex";
// 2. 通过创建一个新的 store 实例
const store = createStore({
});
// 3. 将 store 实例作为插件安装
createApp(App).use(store).mount('#app')
// 创建一个生成Store的
export function createStore() {
return new Store();
}
// 把所有的功能放在 Store 实例中
export class Store {
constructor() {}
// 能做为插件,会调用install方法
install(app, ...options) {
}
}
state
const store = createStore({
// 1. 传入state
state: {
count: 2,
}
});
{{ count }}
{{ $store.state.count }}
思路:
- 通过
Provide/Inject
的技术实现每个组件能够通过useStore
方法获取到store
实例;- 可以将
store
实例挂载到app.config.globalProperties.$store
上,这样通过$store
也可以取到store
实例;state
数据应该封装成响应式;
// 定一个key, 作为 provide/inject 的 key
const storeKey = "store";
export function createStore(options) {
return new Store(options);
}
export class Store {
constructor(options) {
let _this = this;
// 取到state并且变成响应式的数据, 加data这次是修改state方便
this._state = reactive({data: options.state || Object.create(null)});
}
// 属性访问器进行数据封装,取的是_state.data
get state() {
return this._state.data;
}
install(app, ...options) {
// 通过Provide注入给所有组件
app.provide(storeKey, this);
// 挂载在全局对象上,可通过this.$store取到
app.config.globalProperties.$store = this;
}
}
// 返回注入的store对象
export function useStore() {
return inject(storeKey);
}
getters
export default createStore({
getters: {
plusOne: (state) => state.count + 1,
}
});
{{ plusOne }}
{{ $store.getters.plusOne }}
思路:
使用的时候是用属性取值,而定义的时候是函数,所以需要将 函数调用 变成 获取属性。
export class Store {
constructor(options) {
// 将getters函数转成属性
this.getters = {};
// this.getters = {plusOne: }
foreachValue(options.getters, (value, key) => {
// value 是 一个函数
Object.defineProperty(this.getters, key, {
// getters 都是基于 state, 所以传入this.state
// 获取值的时候本质上还是函数调用
get: () => value(_this.state)
})
});
}
}
/**
* 遍历对象属性执行函数
* @param {*} obj 需要遍历的对象
* @param {*} fn 对象的每个key值, 执行函数,传参为 value 和 key
*/
export function foreachValue(obj, fn) {
if (isObject(obj)) {
Object.keys(obj).forEach(key => fn(obj[key], key));
}
}
/**
*
* @param {*} obj 传入参数
* @returns 是否是对象
*/
export function isObject (obj) {
return obj !== null && typeof obj === 'object'
}
mutation
export default createStore({
mutations: {
increment(state, n) {
state.count += n;
},
},
});
思路:
将所有的mutations
放置在_mutations
对象容器中{'方法名': 函数实现 }
,store.commit
调用的时候直接从_mutations
对象容器中获取到对应的函数并且执行
export class Store {
constructor(options) {
// 创建一个_mutation容器
this._mutations = Object.create(null);
foreachValue(options.mutations, (mutation, key) => {
this._mutations[key] = (payload) => {
// 将this重新指向为实例对象store,可以接收两个参数,state和payload
mutation.call(this, this.state, payload);
};
});
}
// 提交mutation的函数
commit = (methodName, payload) => {
// 从_mutation对象容器中找对应的函数
let fn = this._mutations[methodName];
if (fn) {
// 执行fn, 传payload
fn(payload);
}
}
}
Action
export default createStore({
actions: {
incrementAsync ({ commit }, payload) {
return new Promise((resolve, reject) => {
setTimeout(() => {
commit('increment', payload)
resolve()
}, 1000)
})
}
}
});
思路:
所有的actions
放置在_actions
对象容器中{'方法名': 函数实现 }
,store.dispatch
调用的时候直接从_actions
对象容器中获取到对应的函数并且执行
异步的操作需要用到Promise
export class Store {
constructor(options) {
this._actions = Object.create(null);
// 遍历所有的actions函数
foreachValue(options.actions, (action, key) => {
this._actions[key] = (payload) => {
// 调用函数, 将一些上下文传递,因为 异步的操作 不仅是操作 state, 还可能需要操作commit,getters,dispatch等。
let res = action.call(this, {
dispatch: this.dispatch,
commit: this.commit,
getters: this.getters,
state: this.state,
}, payload);
// 函数可能是异步也可能是同步, 如果同步则通过`Promise.resolve`将函数结果转为成功的Promise
if (!isPromise(res)) {
res = Promise.resolve(res);
}
return res;
}
})
}
dispatch = (methodName, payload) => {
let fn = this._actions[methodName];
if (fn) {
let result = fn(payload);
// 返回一个Promise对象,它的状态和结果由result决定
return new Promise((resolve, reject) => {
result.then(
(res) => {
resolve(res);
},
(error) => {
reject(error);
}
);
});
}
}
}
/**
*
* @param {*} val 传入的参数
* @returns 是否是Promise对象
*/
export function isPromise (val) {
return val && typeof val.then === 'function'
}
至此,四部分功能都已经实现了,包括state
, getters
, mutaions
, actions
。
全部代码如下:
import { reactive } from 'vue';
const storeKey = "store";
export function createStore(options) {
return new Store(options);
}
export class Store {
constructor(options) {
this._state = reactive({data: options.state || Object.create(null)});
this.getters = {};
foreachValue(options.getters, (value, key) => {
Object.defineProperty(this.getters, key, {
get: () => value(this.state)
})
});
this._mutations = Object.create(null);
foreachValue(options.mutations, (mutation, key) => {
this._mutations[key] = (payload) => {
mutation.call(this, this.state, payload);
};
});
this._actions = Object.create(null);
foreachValue(options.actions, (action, key) => {
this._actions[key] = (payload) => {
let res = action.call(this, {
dispatch: this.dispatch,
commit: this.commit,
getters: this.getters,
state: this.state,
}, payload);
if (!isPromise(res)) {
res = Promise.resolve(res);
}
return res;
}
})
}
commit = (methodName, payload) => {
let fn = this._mutations[methodName];
if (fn) {
fn(payload);
}
}
dispatch = (methodName, payload) => {
let fn = this._actions[methodName];
if (fn) {
let result = fn(payload);
return new Promise((resolve, reject) => {
result.then(
(res) => {
resolve(res);
},
(error) => {
reject(error);
}
);
});
}
}
get state() {
return this._state.data;
}
install(app, ...options) {
app.provide(storeKey, this);
app.config.globalProperties.$store = this;
}
}
export function useStore() {
return inject(storeKey);
}
export function foreachValue(obj, fn) {
if (isObject(obj)) {
Object.keys(obj).forEach(key => fn(obj[key], key));
}
}
export function isObject (obj) {
return obj !== null && typeof obj === 'object'
}
export function isPromise (val) {
return val && typeof val.then === 'function'
}
modules
我们举的例子有一个子模块
aModule
, 子模块aModule
下有一个子模块bModule
。
export default createStore({
state: {
count: 1,
},
getters: {
plusOne: ...
},
mutations: {
increment: ...
},
actions: {
incrementAsync: ...
},
modules: {
aModule: {
state: {
count: 1,
},
getters: {
plusOneA: ...
},
mutations: {
incrementA: ...
},
actions: {
incrementAsyncA: ...
},
modules: {
bModule: {
state: {
count: 1,
},
getters: {
plusOneB: ...
},
mutations: {
incrementB: ...
},
actions: {
incrementAsyncB: ...
}
},
},
},
},
});
options
参数进行格式化,方便进行后续的数据管理:改造成:
root = {
_rawModule: rootModule({ state, geters, mutations, actions, modules}),
state: rootModule.state,
_childeren: {
aModule: {
_rawModule: aModule({ state, geters, mutations, actions, modules}),
state: aModule.state,
_childeren: {
bModule: {
_rawModule: bModule({ state, geters, mutations, actions, modules}),
state: bModule.state,
_childeren: {}
}
}
}
}
}
代码如下:
export default class ModuleCollection {
constructor(rawRootModule) {
// 这个方法是深度递归遍历的方法,从根部进行开始
this.register([], rawRootModule, false);
}
/**
* 递归进行遍历组织数据
* @param {*} path 模块的名称的深度数组
* @param {*} rawModule 模块对应的原始moudles数据
*/
register(path, rawModule) {
// 先将数据封装成Module,此时的_children还未赋值
const newModule = new Module(rawModule);
if (path.length == 0) {
// 此时说明是根模块, 给root赋值
this.root = newModule; // 这个对象其实就是createStore传入的options得到的数据
} else {
// 此时说明是子模块, 此时需要把自己赋值给父模块的_children
// 找到父模块 , path.slice(0, -1) 是排除最后一个元素的数组(eg. ["aModule", "bModule"] 得到的是 ["aModule"]),就是父模块的路劲
const parent = this.get(path.slice(0, -1));
console.log(path);
console.log(parent);
// 把newModule设置为父module的_children (eg. path[path.length - 1] 就是 "bModule", 值为 模块值 )
parent.addChild(path[path.length - 1], newModule);
}
// 如果有 rawModule 有 moudules 属性, 则递归调用,递归调用时将子模块赋值当前newModule的_children
if (rawModule.modules) {
foreachValue(rawModule.modules, (rawChildModule, key) => {
// path:遍历aModule的时候为 ["aModule"], bModule的时候 ["aModule", "bModule"]
this.register(path.concat(key), rawChildModule);
})
}
}
/**
* 这个方法是一步步深入找到对应的module
* @param {*} path 模块路径数组
* @returns 根据模块路径数组找到对应的module数据
*/
get(path) {
return path.reduce((module, key) => {
return module.getChild(key);
}, this.root);
}
}
export default class Module {
constructor(rawModule) {
// 存储原始的数据
this._rawModule = rawModule;
// 获取state
const rawState = rawModule.state;
this.state = (typeof rawState === "function" ? rawState() : rawState) || {};
// _children的属性
this._children = Object.create(null);
}
// 设置_children属性
addChild(key, module) {
this._children[key] = module;
}
// 获取_children属性
getChild(key) {
return this._children[key];
}
}
_module
进行安装,包括对state
,getter
,mutations
,actions
进行处理改造成:
store._modules.root.state = {
count: 1,
aModule: {
count: 1,
bModule: {
count: 1
}
}
}
store._wrappedGetters = {
plusOne: fun(),
plusOneA: fun(),
plusOneB: fun()
}
store._mutations = {
increment: [fun()],
incrementA: [fun()],
incrementB: [fun()],
}
store._actions = {
incrementAsync: [fun()],
incrementAsyncA: [fun()],
incrementAsyncB: [fun()],
}
代码如下:
export default class Module {
// 省略其他逻辑
// 获取对应的getter函数
forEachGetter(fn) {
if (this._rawModule.getters) {
foreachValue(this._rawModule.getters, fn);
}
}
// 获取对应的actions函数
forEachAction(fn) {
if (this._rawModule.actions) {
foreachValue(this._rawModule.actions, fn);
}
}
// 获取对应的mutations函数
forEachMutation(fn) {
if (this._rawModule.mutations) {
foreachValue(this._rawModule.mutations, fn);
}
}
}
export class Store {
constructor(options) {
// 格式化
this._modules = new ModuleCollection(options);
// 数据初始
this._actions = Object.create(null);
this._mutations = Object.create(null);
this._wrappedGetters = Object.create(null);
// 安装: 处理
const state = this._modules.root.state;
installModule(this, state, [], this._modules.root);
}
}
/**
* 组织数据包括 state,getters, mutations, actions 的整理
* @param {*} store store 对象
* @param {*} rootState 根state对象
* @param {*} path 模块路径数组(eg. ["aModule", "bModule"])
* @param {*} module 当前的模块Module数据
*/
export function installModule(store, rootState, path, module) {
// 是否是根,(空数组是根)
const isRoot = !path.length;
// ******** state 处理逻辑:********
// 如果不是根,就将其按照树状结构挂载到根rootState上
if (!isRoot) {
// 和 Module类似, 将所有的state组织成树状结构
const parentState = getNestedState(rootState, path.slice(0, -1));
const moduleName = path[path.length - 1];
parentState[moduleName] = module.state;
}
// local是指当前 module 的局部数据
const local = {
dispatch: store.dispatch,
commit: store.commit,
getters: store.getters,
};
Object.defineProperties(local, {
state: {
get: () => getNestedState(store.state, path),
},
});
// 局部数据保存到module上
module.context = local;
// ******** getters 处理逻辑:********
module.forEachGetter((getter, key) => {
registerGetter(store, key, getter, local);
});
// ******** actions 处理逻辑:********
module.forEachAction((action, key) => {
registerAction(store, key, action, local);
});
// ******** mutations 处理逻辑:********
module.forEachMutation((mutation, key) => {
registerMutation(store, key, mutation, local);
});
// 遍历子节点
foreachValue(module._children, (childModule, key) => {
installModule(store, rootState, path.concat(key), childModule);
});
}
// 获取对应模块的 state
export function getNestedState(state, path) {
return path.reduce((state, key) => state[key], state);
}
/**
* 将函数添加到_mutations[type]数组中
* @param {*} store store对象
* @param {*} type mutation的key
* @param {*} handler mutation函数
* @param {*} local 局部module数据
*/
function registerMutation(store, type, handler, local) {
// _mutations[type]对应的是一个数组,但是 每个函数操作的是 局部的state数据
const entry = store._mutations[type] || (store._mutations[type] = []);
entry.push(function (payload) {
handler.call(store, local.state, payload);
});
}
/**
* 将函数以属性的形式添加到_wrapperdGetters上
* @param {*} store store对象
* @param {*} type getter的key
* @param {*} rawGetter
* @param {*} local
* @returns
*/
function registerGetter(store, type, getter, local) {
store._wrappedGetters[type] = (store) => {
return getter(
local.state, // local state
local.getters, // local getters
store.state, // root state
store.getters // root getters
);
};
}
/**
* 将函数添加到_actions[type]数组中
* @param {*} store store对象
* @param {*} type action的key
* @param {*} handler action的函数
* @param {*} local 局部数据对象
*/
function registerAction(store, type, handler, local) {
const entry = store._actions[type] || (store._actions[type] = []);
entry.push(function wrappedActionHandler(payload) {
let res = handler.call(
store,
{
dispatch: local.dispatch,
commit: local.commit,
getters: local.getters,
state: local.state,
rootGetters: store.getters,
rootState: store.state,
},
payload
);
if (!isPromise(res)) {
res = Promise.resolve(res);
}
return res;
});
}
_state
和处理_wrapperdGetters
中的数据export function resetStoreState(store, state) {
// 响应式数据
store._state = reactive({
data: state,
});
// _wrappedGetters 转换成 getters 属性
store.getters = {};
const wrappedGetters = store._wrappedGetters;
foreachValue(wrappedGetters, (fn, key) => {
Object.defineProperty(store.getters, key, {
get: () => fn(store),
enumerable: true, // for local getters
});
});
}
export class Store {
constructor(options) {
// 格式化
this._modules = new ModuleCollection(options);
// 数据初始
this._actions = Object.create(null);
this._mutations = Object.create(null);
this._wrappedGetters = Object.create(null);
// 安装: 处理
const state = this._modules.root.state;
installModule(this, state, [], this._modules.root);
// state 和 getter 的 逻辑
resetStoreState(this, state);
}
}
commit
和dispatch
方法 commit = (methodName, payload) => {
let funArr = this._mutations[methodName];
if (funArr) {
// 每个函数都执行
funArr.forEach((fn) => {
fn(payload);
})
}
};
dispatch = (methodName, payload) => {
let funArr = this._actions[methodName];
if (funArr) {
// 所以异步任务都执行完
const result =
funArr.length > 1
? Promise.all(funArr.map((handler) => handler(payload)))
: funArr[0](payload);
return new Promise((resolve, reject) => {
result.then(
(res) => {
resolve(res);
},
(error) => {
reject(error);
}
);
});
}
};
至此,四部分功能都已经实现了,包括state
, getters
, mutaions
, actions
的modules
版本。
import { foreachValue } from "../vuex";
export default class Module {
constructor(rawModule) {
// 存储原始的数据
this._rawModule = rawModule;
// 获取state
const rawState = rawModule.state;
this.state = (typeof rawState === "function" ? rawState() : rawState) || {};
// _children的属性
this._children = Object.create(null);
}
// 设置_children属性
addChild(key, module) {
this._children[key] = module;
}
// 获取_children属性
getChild(key) {
return this._children[key];
}
// 获取对应的getter函数
forEachGetter(fn) {
if (this._rawModule.getters) {
foreachValue(this._rawModule.getters, fn);
}
}
// 获取对应的actions函数
forEachAction(fn) {
if (this._rawModule.actions) {
foreachValue(this._rawModule.actions, fn);
}
}
// 获取对应的mutations函数
forEachMutation(fn) {
if (this._rawModule.mutations) {
foreachValue(this._rawModule.mutations, fn);
}
}
}
import Module from "./module"
import { foreachValue } from "../vuex"
export default class ModuleCollection {
constructor(rawRootModule) {
// 这个方法是深度递归遍历的方法,从根部进行开始
this.register([], rawRootModule, false);
}
/**
* 递归进行遍历组织数据
* @param {*} path 模块的名称的深度数组
* @param {*} rawModule 模块对应的原始moudles数据
*/
register(path, rawModule) {
// 先将数据封装成Module,此时的_children还未赋值
const newModule = new Module(rawModule);
if (path.length == 0) {
// 此时说明是根模块, 给root赋值
this.root = newModule; // 这个对象其实就是createStore传入的options得到的数据
} else {
// 此时说明是子模块, 此时需要把自己赋值给父模块的_children
// 找到父模块 , path.slice(0, -1) 是排除最后一个元素的数组(eg. ["aModule", "bModule"] 得到的是 ["aModule"]),就是父模块的路劲
const parent = this.get(path.slice(0, -1));
console.log(path);
console.log(parent);
// 把newModule设置为父module的_children (eg. path[path.length - 1] 就是 "bModule", 值为 模块值 )
parent.addChild(path[path.length - 1], newModule);
}
// 如果有 rawModule 有 moudules 属性, 则递归调用,递归调用时将子模块赋值当前newModule的_children
if (rawModule.modules) {
foreachValue(rawModule.modules, (rawChildModule, key) => {
// path:遍历aModule的时候为 ["aModule"], bModule的时候 ["aModule", "bModule"]
this.register(path.concat(key), rawChildModule);
})
}
}
/**
* 这个方法是一步步深入找到对应的module
* @param {*} path 模块路径数组
* @returns 根据模块路径数组找到对应的module数据
*/
get(path) {
return path.reduce((module, key) => {
return module.getChild(key);
}, this.root);
}
}
import { reactive } from "vue";
import ModuleCollection from "../vuex-mine/module/module-collection";
const storeKey = "store";
export function createStore(options) {
return new Store(options);
}
export class Store {
constructor(options) {
// 格式化
this._modules = new ModuleCollection(options);
// 数据初始
this._actions = Object.create(null);
this._mutations = Object.create(null);
this._wrappedGetters = Object.create(null);
// 安装: 处理
const state = this._modules.root.state;
installModule(this, state, [], this._modules.root);
// state 和 getter 的 逻辑
resetStoreState(this, state);
}
commit = (methodName, payload) => {
let funArr = this._mutations[methodName];
if (funArr) {
// 每个函数都执行
funArr.forEach((fn) => {
fn(payload);
})
}
};
dispatch = (methodName, payload) => {
let funArr = this._actions[methodName];
if (funArr) {
// 所以异步任务都执行完
const result =
funArr.length > 1
? Promise.all(funArr.map((handler) => handler(payload)))
: funArr[0](payload);
return new Promise((resolve, reject) => {
result.then(
(res) => {
resolve(res);
},
(error) => {
reject(error);
}
);
});
}
};
get state() {
return this._state.data;
}
install(app, ...options) {
app.provide(storeKey, this);
app.config.globalProperties.$store = this;
}
}
export function useStore() {
return inject(storeKey);
}
/**
* 组织数据包括 state,getters, mutations, actions 的整理
* @param {*} store store 对象
* @param {*} rootState 根state对象
* @param {*} path 模块路径数组(eg. ["aModule", "bModule"])
* @param {*} module 当前的模块Module数据
*/
export function installModule(store, rootState, path, module) {
// 是否是根,(空数组是根)
const isRoot = !path.length;
// ******** state 处理逻辑:********
// 如果不是根,就将其按照树状结构挂载到根rootState上
if (!isRoot) {
// 和 Module类似, 将所有的state组织成树状结构
const parentState = getNestedState(rootState, path.slice(0, -1));
const moduleName = path[path.length - 1];
parentState[moduleName] = module.state;
}
// local是指当前 module 的局部数据
const local = {
dispatch: store.dispatch,
commit: store.commit,
getters: store.getters,
};
Object.defineProperties(local, {
state: {
get: () => getNestedState(store.state, path),
},
});
// 局部数据保存到module上
module.context = local;
// ******** getters 处理逻辑:********
module.forEachGetter((getter, key) => {
registerGetter(store, key, getter, local);
});
// ******** actions 处理逻辑:********
module.forEachAction((action, key) => {
registerAction(store, key, action, local);
});
// ******** mutations 处理逻辑:********
module.forEachMutation((mutation, key) => {
registerMutation(store, key, mutation, local);
});
// 遍历子节点
foreachValue(module._children, (childModule, key) => {
installModule(store, rootState, path.concat(key), childModule);
});
}
// 获取对应模块的 state
export function getNestedState(state, path) {
return path.reduce((state, key) => state[key], state);
}
/**
* 将函数添加到_mutations[type]数组中
* @param {*} store store对象
* @param {*} type mutation的key
* @param {*} handler mutation函数
* @param {*} local 局部module数据
*/
function registerMutation(store, type, handler, local) {
// _mutations[type]对应的是一个数组,但是 每个函数操作的是 局部的state数据
const entry = store._mutations[type] || (store._mutations[type] = []);
entry.push(function (payload) {
handler.call(store, local.state, payload);
});
}
/**
* 将函数以属性的形式添加到_wrapperdGetters上
* @param {*} store store对象
* @param {*} type getter的key
* @param {*} rawGetter
* @param {*} local
* @returns
*/
function registerGetter(store, type, getter, local) {
store._wrappedGetters[type] = (store) => {
return getter(
local.state, // local state
local.getters, // local getters
store.state, // root state
store.getters // root getters
);
};
}
/**
* 将函数添加到_actions[type]数组中
* @param {*} store store对象
* @param {*} type action的key
* @param {*} handler action的函数
* @param {*} local 局部数据对象
*/
function registerAction(store, type, handler, local) {
const entry = store._actions[type] || (store._actions[type] = []);
entry.push(function wrappedActionHandler(payload) {
let res = handler.call(
store,
{
dispatch: local.dispatch,
commit: local.commit,
getters: local.getters,
state: local.state,
rootGetters: store.getters,
rootState: store.state,
},
payload
);
if (!isPromise(res)) {
res = Promise.resolve(res);
}
return res;
});
}
export function resetStoreState(store, state) {
// 响应式数据
store._state = reactive({
data: state,
});
// _wrappedGetters 转换成 getters 属性
store.getters = {};
const wrappedGetters = store._wrappedGetters;
foreachValue(wrappedGetters, (fn, key) => {
Object.defineProperty(store.getters, key, {
get: () => fn(store),
enumerable: true, // for local getters
});
});
}
/**
* 遍历对象属性执行函数
* @param {*} obj 需要遍历的对象
* @param {*} fn 对象的每个key值, 执行函数,传参为 value 和 key
*/
export function foreachValue(obj, fn) {
if (isObject(obj)) {
Object.keys(obj).forEach((key) => fn(obj[key], key));
}
}
/**
*
* @param {*} obj 传入参数
* @returns 是否是对象
*/
export function isObject(obj) {
return obj !== null && typeof obj === "object";
}
/**
*
* @param {*} val 传入的参数
* @returns 是否是Promise对象
*/
export function isPromise(val) {
return val && typeof val.then === "function";
}
本文主要是分析代码实现,源码请参阅 Vuex GitHub地址