从实现一个简单的 Vuex 类来了解 Vuex 的工作原理。
实现思路
Vuex 下拥有 install 方法和 Store 类。即创建一个 Vuex 的模块,这个模块导出 install 方法和 Store 类。
install 方法
Vuex 是 Vue 的一个插件,所以需要实现 Vue 插件约定的 install 方 法。
Vue.use 内部会调用 Vuex 对象的 install 方法。
install作用:在 install 中把创建 Vue 根实例时传入的 store 对象注入到 Vue 实例的 $store 中。目的是,在所有组件中可以通过this.$store
获取到 Vuex 中的仓库。
然而在 install 方法中无法拿到 Vue 的实例对象。
Vuex 中通过混入 beforeCreate 来获取 Vue 实例。
部分代码如下:
function install(Vue) {
// 将Vue实例传递给一个局部变量,以便在函数范围内使用
_Vue = Vue;
// 通过混入beforeCreate来获取Vue实例,从而拿到选项中的store对象
_Vue.mixin({
beforeCreate() {
if (this.$options.store) {
_Vue.prototype.$store = this.$options.store;
}
},
});
}
其中,在给 Vue 混入 beforeCreate 钩子函数时,加了一个判断this.$options.store
,
目的是,
保证只在 main.js 文件中创建根 Vue 实例时执行一遍注入操作(_Vue.prototype.$store = this.$options.store
)。在后续创建组件实例时,组件的选项 $options 中没有 router 所以不会再执行注入。
Store 类
首先 store 是一个类,它的构造函数接受一个对象作为参数,这个对象中的属性就是我们熟悉的 state、getters、mutations、actions。
- 实现构造函数,接收 options。
- state 的响应式处理。
- getterrs 的实现。
- commit、dispatch 方法。
注意:
下面代码对 getters 处理中:
其中this.getters = Object.create(null)
,此处不直接写this.getters = getters
,是因为下面的代码中要方法 getters 中的 key 如果这么写的话,会导致 this.getters 和 getters 指向同一个对象,当访问 getters 的 key 的时候,实际上就是访问 this.getters 的 key 会触发 key 属性的 get,会产生死递归。
部分代码如下:
class Store {
constructor(options) {
const { state = {}, getters = {}, mutations = {}, actions = {} } = options;
this.state = _Vue.observable(state);
// 此处不直接 this.getters = getters,会产生死递归
this.getters = Object.create(null);
Object.keys(getters).forEach((key) => {
Object.defineProperty(this.getters, key, {
// 箭头函数 返回通过key在getters中获取到的方法的执行 结果
get: () => getters[key](this.state), // 这里是state如何传到getters中的
});
});
// mutations actions都是内部属性,不希望外部直接访问到
this._mutations = mutations;
this._actions = actions;
}
commit(type, payload) {
this._mutations[type](this.state, payload);
}
dispatch(type, payload) {
this._actions[type](this, payload);
}
}
完整结构
Vue 中使用 Vuex 示例如下:
store–index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count: 0,
msg: "Hello World"
},
getters: {
reverseMsg(state) {
return state.msg.split("").reverse().join("");
},
},
mutations: {
increate(state, payload) {
state.count += payload.num;
},
},
actions: {
increate(context, payload) {
setTimeout(() => {
context.commit("increate", { num: 5 });
}, 2000);
},
},
});
myvuex–index.js
let _Vue = null;
class Store {
constructor(options) {
const { state = {}, getters = {}, mutations = {}, actions = {} } = options;
this.state = _Vue.observable(state);
// 此处不直接 this.getters = getters,会产生死递归
this.getters = Object.create(null);
Object.keys(getters).forEach((key) => {
Object.defineProperty(this.getters, key, {
// 箭头函数 返回通过key在getters中获取到的方法的执行 结果
get: () => getters[key](this.state), // 这里是state如何传到getters中的
});
});
// mutations actions都是内部属性,不希望外部直接访问到
this._mutations = mutations;
this._actions = actions;
}
commit(type, payload) {
this._mutations[type](this.state, payload);
}
dispatch(type, payload) {
this._actions[type](this, payload);
}
}
function install(Vue) {
// 将Vue实例传递给一个局部变量,以便在函数范围内使用
_Vue = Vue;
// 通过混入beforeCreate来获取Vue实例,从而拿到选项中的store对象
_Vue.mixin({
beforeCreate() {
if (this.$options.store) {
_Vue.prototype.$store = this.$options.store;
}
},
});
}
// 导出模块
export default {
Store,
install,
};