【造轮子系列】面试官问:你能手写Vuex4吗 (一)?
Vuex 是一个专为 Vue.js 应用程序开发的状态管理库,它能够集中管理应用中所有组件的状态,并提供了统一的规则来保证状态变更的可追踪性和可维护性。在本文中,我们将讲解如何根据 Vuex 的源码实现一个简单的 Vuex 4。
创建 Store 实例
Vuex 的核心是一个 store 实例,它包含了应用中所有组件的状态,并提供了一些 API 来改变这些状态。我们可以通过创建一个 Store 类来实现一个简单的 Vuex 4,如下所示:
class Store {
constructor(options = {}) {
const {
state = {},
getters = {},
mutations = {},
actions = {},
} = options;
this.state = state;
this.getters = {};
this.mutations = mutations;
this.actions = actions;
}
}
在上面的代码中,我们定义了一个 Store 类,并在构造函数中接收一个 options 参数,该参数包含了 state、getters、mutations 和 actions 等选项。我们将这些选项保存在 store 实例中,并暴露出一些公共的 API。
实现 state 和 getters
state 是一个存储应用中所有组件状态的对象,它是响应式的,当其属性被修改时,所有依赖于它的组件都将得到更新。我们可以通过 Vue 提供的 reactive 函数来实现这个功能,如下所示:
constructor(options = {}) {
// ...
this.state = reactive(state);
this.getters = {};
// ...
}
getters 是一些计算属性,它们从 state 中派生出一些值,这些值可以被其他组件使用。我们可以通过 Object.defineProperty 函数来实现这个功能,如下所示:
constructor(options = {}) {
// ...
this.state = reactive(state);
this.getters = {};
for (let key in getters) {
Object.defineProperty(this.getters, key, {
get: () => getters[key](this.state),
enumerable: true,
});
}
// ...
}
在上面的代码中,我们将 getters 对象中的每个属性都定义为一个 getter 函数,当这些属性被访问时,将会调用对应的 getter 函数,并将 state 作为参数传递进去。
封装 forEachValue 工具方法
后续需要多次使用遍历对象,所以封装工具方法,方便后续代码复用。
// 此公共方法用于遍历获取对象的 value 与 key 传入回调函数
function forEachValue(obj, fn) {
Object.keys(obj).forEach(key => fn(obj[key], key))
}
优化 getters 实现
constructor(options = {}) {
// ...
this.getters = {};
forEachValue(_getters, function (fn, key) {
Object.defineProperty(this.getters, key, {
enumerable: true,
get: () => fn(this.state)
})
})
// ...
}
实现 mutations
mutations 是一些用于改变 state 的函数,它们是同步的,且每个 mutation 都有一个字符串类型的事件类型和一个回调函数。我们可以通过 forEachValue 遍历 mutations ,并触发对应的 mutation,如下所示:
constructor(options = {}) {
// ...
this._mutations = Object.create(null)
forEachValue(mutations, function (mutation, key) {
this._mutations[key] = (value) => {
mutation.call(this, this.state, value)
}
})
// ...
}
实现 actions
actions 是一些用于异步改变 state 的函数,它们可以包含任意异步操作,并在操作完成后通过提交 mutation 来改变 state。如下所示:
constructor(options = {}) {
// ...
this._actions = Object.create(null)
forEachValue(actions, function (action, key) {
this._actions[key] = (value) => {
action.call(this, this, value)
}
})
// ...
}
实现 commit 和 dispatch 方法
commit 方法用于提交一个 mutation,它接收一个字符串类型的事件类型和一个可选的负载对象。我们可以通过调用对应的 mutation 回调函数来实现这个功能,如下所示:
commit = (type, payload) => {
if(!this._mutations[type]){
console.error(`Unknown mutation type: ${type}`);
}
this._mutations[type](payload)
}
dispatch 方法用于分发一个 action,它接收一个字符串类型的事件类型和一个可选的负载对象。我们可以通过调用对应的 action 函数来实现这个功能,如下所示:
dispatch = (type, payload) => {
if (!this._actions[type]) {
console.error(`Unknown action type: ${type}`);
}
this._actions[type](payload)
}
实现 install 方法
最后,我们可以通过定义一个 install 方法来将 Vuex 4 安装到 Vue.js 应用程序中,如下所示:
class Store {
constructor(options) {
install(vue, injectKey = storeKey) {
// 使用 vue3 的 provide/inject 实现
vue.provide(injectKey, this) // injectKey 注入名称,在 userStore 获取时会用
vue.config.globalProperties.$store = this // 将 $store 加到 vue3 实例上 (兼容 vue2)
}
}
}
在上面的代码中,我们将 $store 对象注册为一个全局属性,并使用 provide/inject 机制将 $store 对象注入到应用程序中的所有组件中。这样,我们就可以在组件中通过 this.$store 来访问 store 对象。
实现 useStore
const storeKey = 'store'
export function useStore(injectKey = storeKey) {
return inject(injectKey)
}
总结
我们根据 Vuex 4 的源码实现了一个简易的 Vuex4。我们首先了解了 Vuex 的核心概念,包括 state、getter、mutation 和 action,然后使用 Vue 3 的新特性,如 reactive、 provide/inject 等,实现了一个简单的状态管理库。
目前这个实现并不完整,还缺少了一些高级功能,例如模块化、namespace 和严格模式等,我们在后续章节中将继续实现这些功能,通过这个简易的实现帮助更好地理解 Vuex 的工作原理,从而在实际开发中更加自如地使用 Vuex。
如果你对 Vuex 的实现原理还有更深入的探究,可以通过阅读 Vuex 源码来进一步了解其内部实现细节。
完整代码
系列文章
我的更多前端资讯
欢迎大家技术交流 资料分享 摸鱼 求助皆可 —链接
转载自:https://juejin.cn/post/7244436362979459128