手写vuex
任务分析
实现一个插件:声明Store类,挂载$store
Store具体实现:
- 创建响应式的state,保存mutations、actions和getters
- 实现commit根据用户传入type执行对应mutation
- 实现dispatch根据用户传入type执行对应action,同时传递上下文
- 实现getters,按照getters定义对state做派生
定义vuex.js
store/vuex.js
let Vue;
class Store{
/**
* 下面构造参数options格式:
* {
state: {},
mutations: {},
actions: {},
getters: {}
}
*/
constructor(options){
/*
下面mutations格式
mutations: {
add(state){
state.count++
}
}*/
this._mutations = options.mutations
this._actions = options.actions
this._wrappedGetters = options.getters
// 响应化处理state
this.state = new Vue({
// 根组件直接赋值,不用函数
data: options.state
})
}
// type: add
// playload是参数
commit(type, payload){
// 拿到用户在index定义的mutations
const entry = this._mutations[type];
if(entry){
entry(this.state, payload)
}
}
dispatch(type, payload){
// 拿到用户在index定义的actions
const entry = this._actions[type];
if(entry){
// this就有dispatch/commit
entry(this, payload)
}
}
}
function install(_Vue){
Vue = _Vue
Vue.mixin({
beforeCreate(){
if(this.$options.store){
Vue.prototype.$store = this.$options.store
}
}
})
}
export default{
Store,
install
}
store/index.js
import Vue from 'vue'
import Vuex from './vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count: 10
},
mutations: {
add(state){
state.count++
}
},
actions: {
add({ commit }){
setTimeout(() => {
commit('add')
}, 1000)
}
}
})
效果
但是点击dispatch时发现报错。原因是vuex.js的commit没有绑定this,如果commit在定时器提交,this就会乱
修改代码,下面代码放在vuex.js的store的构造里
this.commit = this.commit.bind(this)
this.dispatch = this.dispatch.bind(this)
再点击dispatch就会发现等待一秒count+1了
继续优化
将vuex.js的store的构造中的
// 响应化处理state
this.state = new Vue({
// 根组件直接赋值,不用函数
data: options.state
})
换成
// 严格模式下官方定义了watch,只要有任何修改就报错
this._vm = new Vue({
data: {
// 加两个$,Vue不做代理,无法通过_vm拿到
$$state: options.state,
}
})
再定义读取函数作为vuex.js的store的普通函数
// 存取器
// this.state返回this._vm._data.$$state
get state(){
// _data可换成$data
return this._vm._data.$$state
}
set state(v){
console.error('不允许直接修改哦')
}
此时,直接this.$store.state.count = {}会报错
加入getters支持
也是完整demo代码
store/vuex.js
let Vue;
class Store{
/**
* options:
* {
state: {},
mutations: {},
actions: {},
getters: {}
}
*/
constructor(options){
/*
下面mutations格式
mutations: {
add(state){
state.count++
}
}*/
this._mutations = options.mutations
this._actions = options.actions
this._wrappedGetters = options.getters
// 定义computed选项
const computed = {}
this.getters = {}
// computed无参,这里有state需要进行封装
// doubleCounts(state){ return state.count *= 2 }
const store = this
Object.keys(this._wrappedGetters).forEach(key => {
// 获取用户定义的getter,注意fn带了state参数
const fn = store._wrappedGetters[key]
// 转换为computed可以使用的无参数形式
computed[key] = function(){
// 把this.state传递进去
return fn(store.state)
}
// 为getter定义只读属性
Object.defineProperty(store.getters, key, {
get: () => store._vm[key]
})
})
/* // 响应化处理state
this.state = new Vue({
// 根组件直接赋值,不用函数
data: options.state
})
*/
// 严格模式下官方定义了watch,只要有任何修改就报错
this._vm = new Vue({
data: {
// 加两个$,Vue不做代理,无法通过_vm拿到
$$state: options.state,
},
computed
})
// 绑定commit.dispatch的上下文store实例,没有绑定this,如果commit在定时器提交,this就会乱
this.commit = this.commit.bind(this)
this.dispatch = this.dispatch.bind(this)
}
// 存取器
get state(){
// _data可换成$data
return this._vm._data.$$state
}
set state(v){
console.error('不允许直接修改哦')
}
// type: add
// playload是参数
commit(type, payload){
// 拿到用户在index定义的mutations
const entry = this._mutations[type];
if(entry){
// this.state返回this._vm._data.$$state
entry(this.state, payload)
}
}
dispatch(type, payload){
// 拿到用户在index定义的actions
const entry = this._actions[type];
if(entry){
// this就有dispatch/commit
entry(this, payload)
}
}
}
function install(_Vue){
Vue = _Vue
Vue.mixin({
beforeCreate(){
if(this.$options.store){
Vue.prototype.$store = this.$options.store
}
}
})
}
export default{
Store,
install
}
store/index.js
import Vue from 'vue'
import Vuex from './vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count: 10
},
mutations: {
add(state){
state.count++
}
},
actions: {
add({ commit }){
setTimeout(() => {
commit('add')
}, 1000)
}
},
getters: {
// 返回2倍的count
doubleCounts(state){
return state.count * 2
}
}
})
效果
1
1
1
1
1
1
1
1
1
1