使用vue过程中难免会用到vuex,但是很多时候我们只知道如何使用它但却不明白内部运行机制,下面我们来简单手写一下vuex的实现过程。
简介
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。其集中式的存储了应用所有组件的状态,并且制定了相对应的规则以便保证组件状态可以朝着可预测的方向发生变化。
首先需要了解vuex的几个核心概念:
- State 单一状态树,唯一数据源
- Mutation 状态更改函数
- Action 包含任意异步操作
- Getter state中派生出一些状态,类似计算属性
- Module store模块分割机制,
- store 包含以上概念的容器
对这些概念不清楚的可以观看vuex官方进行进一步了解:https://vuex.vuejs.org/zh/
vuex原理解析
首先vuex是一个插件,我们需要去声明一个store类,还要有个install方法去挂载$store。
store的具体实现过程:
1、创建响应式的state,保存mutations,actions与getters。
2、实现commit根据用户传入type执行对应mutation。
3、实现dispatch根据传入的type生成对应的action,同时传递数据。
4、实现getters,按照getters定义对state派生数据。
首先设计vuex基本数据:
import Vue from 'vue'
import Vuex from './vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
counter: 0,
},
mutations: {
add(state) {
state.counter++
}
},
actions: {
// 参数怎么来的?
add({ commit }) {
// 业务逻辑组合或者异步
setTimeout(() => {
commit('add')
}, 1000);
}
},
getters: {
doubleCounter: state => {
return state.couter*2;
},
},
modules: {
}
})
1.对store类的声明,install方法实现。
let Vue;
// 声明Store类
class Store {
constructor(options = {}) {
this._vm = new Vue({
// data中的值都会做响应化处理
data: {
// 相当于总线
$$state:options.state
}
});
}
get state() {
// 存取器使之成为只读
return this._vm._data.$$state
}
set state(v) {
console.error('please use replaceState to reset state');
} }
function install(_Vue) {
Vue = _Vue;
Vue.mixin({
beforeCreate() {
if (this.$options.store) {
// this.$options为Vue 实例的初始化选项
Vue.prototype.$store = this.$options.store;
}
}
});
}
export default { Store, install };
这里有两点值得注意,源码中state利用vue的data的响应式。利用存取器,把state设置成只读属性。
这里双$定义state第一次看上去肯定有点不解,这里是为了Vue不做代理。
2.实现commit:根据用户传入type获取并执行对应mutation
class Store {
constructor(options = {}) {
// 保存用户配置的mutations选项
this._mutations = options.mutations || {}
}
commit(type, payload) {
// 获取type对应的mutation
const entry = this._mutations[type]
if (!entry) {
console.error(`unknown mutation type: ${type}`);
return
}
// 指定上下文为Store实例
// 传递state给mutation entry(this.state, payload);
entry(this.state, payload)
}
}
这里的entry是mutations里面定义的add方法,payload是传入的参数。
3.实现actions:根据用户传入type获取并执行对应action
class Store {
constructor(options) {
this._actions = options.actions
// 锁死commit,dispatch函数this指向
const store = this
const {commit, dispatch} = store
this.commit = function boundCommit(type, payload) {
commit.call(store, type, payload)
}
this.dispatch = function boundDispatch(type, payload) {
dispatch.call(store, type, payload)
}
}
// dispatch,执行异步任务或复杂逻辑
dispatch(type, payload) {
// 1.获取action
const entry = this._actions[type]
if (!entry) {
console.error('哎呦,没有这个action');
return;
}
// 异步结果处理常常需要返回Promise
return entry(this, payload)
}
}
这里对dispatch和commit做了this的绑定,必须绑定commit上下文否则action中调用commit时可能出问题
3.实现getters:state的动态计算属性
class Store {
constructor(options) {
const store = this
this._wrappedGetters = options.getters
// 定义computed 选项
const computed = {}
this.getters = {}
Object.keys(this._wrappedGetters).forEach(key => {
// 获取用户定义的getter
const fn = store._wrappedGetters[key]
// 转换为computed可以使用无参数形式
computed[key] = function() {
return fn(store.state)
}
// 为getters定义只读属性
Object.defineProperty(store.getters, key, {
get: () => store._vm[key]
})
})
this._vm = new Vue({
// data中的值都会做响应化处理
data: {
$$state: options.state
},
// 利用vue的computed计算属性
computed
})
}
这里看起来复杂仔细拆分每一个看,其实很简单。这里值得注意的是store._vm[key]是响应的,使用Object.defineProperty设置只读属性这样随着store._vm[key]变化store.getters[key]也会响应变化,这里的get可以理解成vue中的computed属性内容。
最终实现效果