1、简版Vuex实现思路
和实现简版Vue Router类似,实现简版的Vuex,可以按照下面的步骤来:
-
MyVuex是一个插件:以
Vue.use的方式安装 -
和官方Vuex
const store = Vuex.Store({...})使用方法类型,MyVuex最终导出一个对象,并提供MyStore属性` -
state,getters, mutations和actions这四个核心概念的实现:
- state: 设为Vue的data, 实现响应式
- getters: 只读属性,不可修改
- mutations和actions都是对象的方法,只需执行对于的方法函数即可
-
commit和dispatch的实现
- commit的参数是state
- dispatch的参数是ctx({state, getters, commit, dispatch...})
-
把MyStore选项混入到原型上
2、实现简版Vuex
按照上面的实现思路,实现简版Vuex的完整代码如下所示:
// stote/my-vuex.js
let Vue
function install(_Vue) {
Vue = _Vue
// 这样store执行的时候,就有了Vue,不用import
// 这也是为啥Vue.use必须在新建store之前
Vue.mixin({
beforeCreate() {
// 根组件才把store选项混入到Vue原型上
if (this.$options.myStore) {
Vue.prototype.$myStore = this.$options.myStore
}
}
})
}
class MyStore {
constructor(opts = {}) {
this.$opts = opts
// 利用vue数据响应
this.state = new Vue({
data: this.$opts.state
})
// 初始化getters, mutations和actions
this.$opts.getters && this.handlerGetters(this.$opts.getters)
this.mutations = this.$opts.mutations || {}
this.actions = this.$opts.actions || {}
}
handlerGetters(getters) {
this.getters = {}
// 定义只读的属性
Object.keys(getters).forEach(key => {
Object.defineProperty(this.getters, key, {
get: () => {
// 执行getters中的方法
return getters[key](this.state)
}
})
})
}
// 触发mutaions,需要实现commit
commit = (type, arg) => {
this.mutations[type](this.state, arg)
}
// 触发actions,需要实现dispatch
dispatch = (type, arg) => this.actions[type]({ commit: this.commit, state: this.state, getters: this.getters, dispatch: this.dispatch }, arg)
}
export default { MyStore, install }
// stote/test-my-vuex.js
import Vue from 'vue'
import MyVuex from './my-vuex'
Vue.use(MyVuex)
export default new MyVuex.MyStore({
state: {
myCount: 0
},
getters: {
myLeft(state) {
return 10 - state.myCount
},
},
mutations: {
myAdd(state) {
state.myCount += 1
},
},
actions: {
myAdd({getters, commit}) {
if (getters.myLeft > 0) {
commit('myAdd')
return true
}
return false
},
myAsyncAdd({dispatch}) {
return new Promise(resolve => {
setTimeout(() => {
resolve(dispatch('myAdd'))
}, 1000)
})
},
},
})
// main.js
import Vue from 'vue'
import App from './App.vue'
import myStore from './store/test-my-vuex'
Vue.config.productionTip = false
new Vue({
myStore,
render: h => h(App)
}).$mount('#app')
<!--TestMyVuex.vue-->
<template>
<div>
<h3>测试我手写的简版Vuex</h3>
<p>手榴弹扔了{{$myStore.state.myCount}}</p>
<p>手榴弹还剩{{$myStore.getters.myLeft}}</p>
<button @click="Throw">扔一个手榴弹</button>
<button @click="asyncThrow">蓄力扔一个手榴弹</button>
</div>
</template>
<script>
export default {
methods: {
Throw() {
const result = this.$myStore.dispatch("myAdd");
if (!result) {
alert("没手榴弹了");
}
},
async asyncThrow() {
const result = await this.$myStore.dispatch("myAsyncAdd");
if (!result) {
alert("没手榴弹了");
}
},
}
};
</script>