本文参照vuex 4的基本功能,模拟实现了其主要功能,并提供了关键的代码。
本文是在Vue 3.0上通过其Composition API模拟Vuex 4.0的基本功能。
Vuex主要包括Actions,Mutations和State三部分,在4.0后增加了getters,但getters本身只是函数式编程思想的延伸,让组件通过方法来获取state的内容。
Vuex中函数调用流程是组件通过dispatch方法调用Actions中的方法,Actions中的方法通过commit方法,调用Mutations中的方法,由Mutation是中的方法去修改State中的响应式数据,响应式数据的修改会通过Vue的render方法反馈到页面上。
(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.data, 这样做的好处是如果用户修改响应式数据,vuex并不需要重新调用vue的reactive方法生成响应式数据。
为了保证store对象的state属性可以被访问,用户并不需要在访问时个加上data的key值,所以为state属性作了一个get方法
_mutations和_actions对象中包含我们的自定义方法,但是其并不包含prototype,因为他们的任务只是用来接收用户传入的方法。
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;
}
实现的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) ;
}
}
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;
}
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;
}
export function createGetters(getters, store){
const _getters = {}
foreachKeyValue(getters, (key, fn) =>{
Object.defineProperty(_getters, key, {
get: ()=> fn(store.state, getters)
});
});
return _getters;
}