【Vuex原理】从零实现一个基础版Vuex
回顾vuex使用
- 创建
store/index.js
- 使用
Vuex.store()
创建vuex
实例 - 使用
Vue.use(Vuex)
注册Vuex
- 使用
new Vue({store})
使用vuex
- 然后就可以在组件中使用
this.$store
访问共享的数据了,以及调用mutations
,actions
去修改共享数据。
实现state
vuex作为一个插件,需要在vue中使用,需要按照vue插件开发规范去实现。在vue文档中写到,注册插件需要使用提供一个install方法。
mixin
可以将一些函数合并到每个组件的生命周期中,所以利用beforeCreate
将共享数据store
挂载到每个组件上。- 只有
跟组件
上可以访问this.$options.strore
,又因为父组件比子组件先创建,为了让子组件也可以访问store
,所以使用this.$parent.$store
进行store
绑定。 - 然后就可以像使用
vuex
一样,使用myVuex
进行数据共享。
// MyVuex.js
const install = (Vue, options) => {
Vue.mixin({
beforeCreate() {
// 将store挂载到每个vue组件上
if (this.$options?.store) {
this.$store = this.$options.store;
} else {
this.$store = this.$parent.$store
}
}
})
}
class Store {
constructor(options) {
// 将传递进来的state挂载到this中
this.state = options.state;
}
}
export default {
install,
Store
}
实现getters
在vue组件中,可以使用
this.$store.getters.xxx
来访问共享的store
中的xxx
变量。
修改Store类
- 为
store
对象添加一个getters
的空对象属性,并使用Object.defineProperty
方法将监听store.getters
的属性,当触发get
的时候,就会将调用options.getter
上对应的方法,并将store.state
传递给该函数。
class Store {
constructor(options) {
// 将传递进来的state放到Store上
this.state = options.state;
// 1.拿到传递进来的getters
let getters = options.getters || {};
// 2.在Store上新增一个getters的属性
this.getters = {};
// 3.将传递进来的getters中的方法添加到当前Store的getters上
for (let key in getters) {
Object.defineProperty(this.getters, key, {
get: () => {
return getters[key](this.state);
}
})
}
}
}
实现mutations
mutations
用于同步修改store
中的数据,使用this.$store.commit
来执行该同步函数。
- 创建
initMutations
函数与commit
函数 - 在
initMutations
函数中,再根据options
上面传递过来的mutations
,为store
对象添加一个mutations
空对象,并为其添加方法,并将store
上的state
传递给该方法。所以开发者在使用的使用就可以得到state
对象。并在构造函数中执行。 - 创建
commit
方法,使得开发者可以通过this.$store.commit
来访问store
上的值。
注意
问题:这时候我们会发现,执行了commit
方法后,页面并没有发生变化,但是打印发现数据确实变了,这是因为没有将视图与数据进行双向绑定。
解决:需要将this.state = options.state;
替换为Vue.util.defineReactive(this, 'state', options.state)
。这是vue提供的一个可以将视图与数据进行绑定的api
。它可以将我们的state
与每个vue
组件进行绑定,实现视图根据实际实时更新。
// MyVuex.js
const install = (Vue, options) => {
Vue.mixin({
beforeCreate() {
// 将store挂载到每个vue组件上
if (this.$options?.store) {
this.$store = this.$options.store;
} else {
this.$store = this.$parent.$store
}
}
})
}
class Store {
constructor(options) {
// 将传递进来的state放到Store上
// this.state = options.state;
Vue.util.defineReactive(this, 'state', options.state);
// 将传递进来的getters放到Store上
this.initGetters(options);
// 将传递进来的mutations放到Store上
this.initMutations(options);
}
commit(type, payload) { // 'addNum', 10
this.mutations[type](payload); // this.mutations[addNum](10);
}
initMutations(options) {
// 1.拿到传递进来的mutations
let mutations = options.mutations || {};
// 2.在Store上新增一个mutations的属性
this.mutations = {};
// 3.将传递进来的mutations中的方法添加到当前Store的mutations上
for (let key in mutations) {
this.mutations[key] = (payload) => {
mutations[key](this.state, payload);
}
}
}
}
export default {
install,
Store
}
实现actions
actions
是用于创建异步修改store
中数据的方法,通过this.$store.dispatch
可以执行异步函数修改store
。
- 创建
dispath
函数与initActions
函数 - 在
initActions
中,根据options
中传递过来的actions
,为store
创建一个actions
空对象,并根据options.actions
,为store.actons
添加方法,并将this作为参数传递给action方法,这样用户在定义actions
方法的时候就可以拿到commit
方法了。 - 在
dispatch
中,根据函数名,调用store.actons
上对应的方法。
注意:需要使用箭头函数
,防止开发者在外部定义的actions
方法拿不到this
。
dispatch = (type, payload) => { // 'asyncAddAge', 5
this.actions[type](payload); // this.actions[asyncAddAge](5);
}
initActions = (options) => {
// 1.拿到传递进来的actions
let actions = options.actions || {};
// 2.在Store上新增一个actions的属性
this.actions = {};
// 3.将传递进来的actions中的方法添加到当前Store的actions上
for(let key in actions){
this.actions[key] = (payload)=>{ // 5
actions[key](this, payload); // asyncAddAge(this, 5);
}
}
}
完整代码
import Vue from 'vue'
const install = (Vue, options) => {
Vue.mixin({
beforeCreate() {
if (this.$options?.store) {
this.$store = this.$options.store;
} else {
this.$store = this.$parent.$store;
}
}
});
}
class Store {
constructor(options) {
Vue.util.defineReactive(this, 'state', options.state);
this.initGetters(options);
this.initMutations(options);
this.initActions(options);
}
dispatch = (type, payload) => {
this.actions[type](payload);
}
initActions(options) {
let actions = options.actions || {};
this.actions = {};
for (let key in actions) {
this.actions[key] = (payload) => {
actions[key](this, payload);
}
}
}
commit = (type, payload) => {
this.mutations[type](payload);
}
initMutations(options) {
let mutations = options.mutations || {};
this.mutations = {};
for (let key in mutations) {
this.mutations[key] = (payload) => {
mutations[key](this.state, payload);
}
}
}
initGetters(options) {
let getters = options.getters || {};
this.getters = {};
for (let key in getters) {
Object.defineProperty(this.getters, key, {
get: () => {
return getters[key](this.state);
}
})
}
}
}
export default {
install,
Store
}
点赞收藏不迷路
转载自:https://juejin.cn/post/7157616962074312734