前言
60 行实现了部分功能,并没有全部实现,但是代码非常简单
功能包括:
- State
- Mutations
- dispatch 【注:vuex中是操作异步,我这里并没有这样写,目的是实现功能】
如图:
mini-vuex 工作原理
- 初始化 Store:首先,在 Vue 应用中初始化 Vuex 的 Store。这个 Store 包含了应用的所有状态、操作。
- 组件中访问状态 :在 Vue 组件中,通过
this.$store
访问到 Vuex 的 Store 对象。这使得组件能够直接从 Store 中读取状态。 - 提交 Mutation 修改状态:当组件需要修改状态时,它会通过提交 Mutation 的方式来请求状态的修改。Mutation 是同步操作,它们包含了状态的修改逻辑。
- 状态变更:当一个 Mutation 被提交后,它会修改 Store 中的状态。这个状态的修改是响应式的,所以任何依赖于这个状态的组件都会立即更新。
实现mini-vuex
清楚了工作原理这些我们现在分步来实现 mini-vuex
一、初始化 Store
想要使用 Vue.use()
那就必须得实现一个 install 那么我们来实现这一段,代码如下:
js
let Vue
export Class Store {
}
function install(_Vue) {
Vue = _Vue;
Vue.mixin({
beforeCreate: vuexInit,
});
function vuexInit() {
const options = this.$options;
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;
}
}
}
export default {
Store,
install,
};
vuexInit
这个方法的主要目的是在 Vue 组件中初始化 Vuex store。
在 Vue 组件的选项对象中,如果存在 store
属性,那么这个方法会检查 store
属性的类型。如果 store
是一个函数,那么它会调用这个函数并将返回的结果赋值给 this.$store
。如果 store
不是一个函数,那么它会直接将 store
的值赋给 this.$store
。
如果在 Vue 组件的选项对象中没有 store
属性,但是存在 parent
属性,并且 parent
对象有 $store
属性,那么这个方法会将 parent
的 $store
属性的值赋给 this.$store
。使得在组件中可以通过 this.$store
访问到 Vuex store。
二、组件中访问状态
简单理解就是 我想通过 this.$store.state.xxx
访问到某个具体属性,上述代码中,我们是并没有对 Store
这个类进行赋值的,那么接下来,我们就实现这个功能
js
let Vue;
export class Store {
constructor({ state = {}, mutations = {} } = {}) {
// 使用一个 Vue 实例来存储状态树
this._vm = new Vue({
data: state,
});
}
get state() {
return this._vm._data;
}
}
我们在外部存储了一份 Vue ,在我们 install 的时候 Vue = _Vue
完成存储,所以我们在 constructor
中定义了一个存储状态树将 state 保存在Vue上,实现了响应式,通过我们的 get关键字
,当你访问 this.state
会执行这个 get state()
返回 this._vm._data
,即 Vue 实例的数据对象。这样,通过这个 getter 函数,你可以直接访问 Vue 实例的数据对象,而无需直接访问 Vue 实例的 _data
属性
三、提交 Mutation 修改状态 和 状态变更
我们实现 this.$store.dispatch("事件名","值")
,通过他来改变和更新我们的状态
js
let Vue;
export class Store {
constructor({ state = {}, mutations = {} } = {}) {
// 将 dispatch 绑定到自身
const dispatch = this.dispatch;
this.dispatch = (...args) => {
dispatch.apply(this, args);
};
// 使用一个 Vue 实例来存储状态树
this._vm = new Vue({
data: state,
});
this._mutations = Object.create(null); // 用于存储可调用的 mutation 函数
this._setupMutations(mutations); // 初始化 mutations
}
get state() {
return this._vm._data;
}
_setupMutations(mutations) {
this._mutations = Array.isArray(mutations)
? mergeObjects(mutations, true)
: mutations;
}
// 提交 Mutation 修改状态 状态变更
dispatch(type, ...payload) {
const mutation = this._mutations[type];
const state = this.state;
if (!mutation) {
throw new Error(`[vuex] Unknown action type: ${type}`);
}
mutation(state, ...payload);
}
}
首先将当前对象的 dispatch
方法引用赋值给 dispatch
变量。然后,将 this.dispatch
重新定义为一个新的函数,这个新的函数接收任意数量的参数(由 ...args
表示),并调用 dispatch
函数,同时保证 dispatch
函数内部的 this
上下文仍然指向当前对象。
然后实现我们的 dispatch
,我们将取出对应的 mutation
函数,将 state 传入 mutation
于是 我们可以看这一段
js
const mutations = {
ADD_TODO: (state, todo) => {
state.todos.push(todo);
},
};
如上面代码,我们会在这里 (state,payload)=>{}
访问到值并调用,state 指向的就是响应式,后面就是你要传递的值
小结
到这里我们的 mini-vuex
就完成了,感兴趣的朋友可以去实现一个 todo 案例,那么我们总结一下几点,可能面试能说的:
- Vuex 通过混入的方式实现挂载
this.$store
- Vuex 内部保存了一份Vue,通过 new Vue 实现数据响应式 所以变更状态,才可以更改视图