概要
本文参照vuex 4的基本功能,模拟实现了其主要功能,并提供了关键的代码。
本文是在Vue 3.0上通过其Composition API模拟Vuex 4.0的基本功能。
Vuex的工作流程
Vuex主要包括Actions,Mutations和State三部分,在4.0后增加了getters,但getters本身只是函数式编程思想的延伸,让组件通过方法来获取state的内容。
Vuex中函数调用流程是组件通过dispatch方法调用Actions中的方法,Actions中的方法通过commit方法,调用Mutations中的方法,由Mutation是中的方法去修改State中的响应式数据,响应式数据的修改会通过Vue的render方法反馈到页面上。
Vuex代码实现流程
(1) 在store.js中, 调用createStore方法,将用户自定义的state, actions, mutations和getters 对象作为参数传入。
import { createStore } from 'vuex'
import state from './state'
import actions from './actions'
import mutations from './mutations'
import getters from './getters'
export default createStore({
state,actions,mutations, getters
})
(2) 在main.js中,导入createStore返回结果,并通过use方法全局注册。
import { createApp } from 'vue'
import App from './App.vue'
import store from './store'
createApp(App).use(store).mount('#app')
代码模拟实现
通过上面的流程,我们可以看出 createStore 返回了一个store对象,该对象一定包含install方法。
我们将createStore 打印出来可以看到,store对象包含install方法,我们传入的state,mutations,actions和getters被转换成了_state, _mutations, _actions和getters对象属性。
state
对于_state, 用户的数据是赋值给_state.data, 这样做的好处是如果用户修改响应式数据,vuex并不需要重新调用vue的reactive方法生成响应式数据。
为了保证store对象的state属性可以被访问,用户并不需要在访问时个加上data的key值,所以为state属性作了一个get方法
mutations和actions
_mutations和_actions对象中包含我们的自定义方法,但是其并不包含prototype,因为他们的任务只是用来接收用户传入的方法。
getters
getter对象包含用户传入的方法,并为这些方法增加了get属性。
代码实现
基于上面的逻辑,createStore 和useStore方法如下:
import { inject } from "vue";
import Store from "./store"
export function createStore(options){
return new Store(options);
}
export function useStore(){
const store = inject("store");
return store;
}
- createStore方法接收用户如传入的atcions,mutations,state和getters对象,返回一个Store类的实例。
- useStore可以在组件中调用,获取当前的Store类实例,它是通过vue3的inject API实现Store对象的注入。
实现的Store类代码如下:
import { reactive } from "vue";
import { createMutations, createActions, createGetters} from "./creator";
export default class Store{
constructor(options){
const {actions, mutations, getters, state} = options;
this._state = reactive({
data: state
});
const store = this;
this._mutations = createMutations(mutations, store);
this._actions = createActions(actions, store);
this.getters = createGetters(getters, store);
this.commit = this.commit.bind(store);
this.dispatch = this.dispatch.bind(store);
}
get state(){
return this._state.data;
}
commit(key, payload){
this._mutations[key](payload);
}
dispatch(key, payload){
this._actions[key](payload);
}
install(app){
app.provide("store", this) ;
}
}
- Store类的install方法将Store类的实例注册到全局;
- Store的类构造方法接收传入的atcions,mutations,state和getters
- 调用vue3的reactive方法为{data: state}对象生成响应式数据;
- 生成state属性的get方法,用户可以通过调用该get方法直接获取 this._state.data;的值;
- 调用createMutations方法创建 this._mutations属性;
- 调用createActions方法创建 this._actions属性;
- 调用createGetters方法创建 this.getters 属性;
- commit方法即根据用户传入的key和参数payload,调用_mutations对象中的指定方法,需要将函数内容的this强制设置为当前Store对象;
- dispatch方法即根据用户传入的key和参数payload,调用_actions对象中的指定方法,需要将函数内容的this强制设置为当前Store对象;
createMutations,createActions,createGetters方法,通过调用foreachKeyValue方法,遍历出入的mutations对象,actions对象和getters对象中的方法,将其赋值到Store实例的各个属性中。
export function foreachKeyValue(obj, fn){
Object.keys(obj).forEach(key => fn(key, obj[key]));
}
export function createMutations(obj, store){
const _mutations = Object.create(null);
foreachKeyValue(obj, (key, fn) => {
_mutations[key] = function(payload){
fn.apply(store, [store.state, payload]);
}
});
return _mutations;
}
- createMutations方法接收两个参数,一个参数是用户传入的mutations对象,另一个参数是当前Store类的实例;
- 创建一个无prototype的_mutations对象;
- 调用foreachKeyValue方法,遍历参数mutations对象的所有用户自定义方法,按照相同的key,在_mutations上创建一个函数,该方法通过apply调用用户自定义方法,并将用户自定义方法的this限定为当前Store类实例,传入当前的state和用户自己的参数。
export function createActions(obj, store){
const _actions = Object.create(null);
foreachKeyValue(obj, (key, fn) => {
_actions[key] = function(payload){
fn.apply(store, [store, payload])
}
});
return _actions;
}
- createActions方法接收两个参数,一个参数是传入的actions对象,另一个参数是当前Store类的实例;
- 创建一个无prototype的_actions 对象;
- 调用foreachKeyValue方法,遍历参数actions对象的所有用户自定义方法,按照相同的key,在_actions上也创建一个方法,该方法通过apply调用用户自定义方法,并将用户自定义方法的this限定为当前Store类实例,传入当前的Store类实例和用户自己的payload参数。
export function createGetters(getters, store){
const _getters = {}
foreachKeyValue(getters, (key, fn) =>{
Object.defineProperty(_getters, key, {
get: ()=> fn(store.state, getters)
});
});
return _getters;
}
- createGetters方法接收两个参数,一个参数是传入的getters对象,另一个参数是当前Store类的实例;
- 创建_getters 对象;
- 调用foreachKeyValue方法,遍历参数getters对象的所有用户自定义方法,按照相同的key,在_getters 上创建自定义属性,在该属性的get方法中调用用户的自定义方法,将state和getters对象作为参数一并传入。