一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情。
介绍
近来被问到不使用vuex,那你可以自己实现一个vuex吗?
以前是经常使用vuex,但是让自己实现一个vuex,还真没有考虑到。于是先去查阅了一下资料,学习一下,然后自己来实现一个简易版的vuex。
接下来就一步一步实现一个简易版vuex
创建工程
用vue-cli脚手架快速创建一个基础工程
创建一个plugin
平时我们使用vuex的时候,是先导入vuex,然后使用Vue.use(vuex)来注册这个插件。然后我们就可以使用vuex了。
我们自己实现一个简易版的vuex,也可以借鉴这种思路,以vue插件的形式来实现,开发一个vue的插件
-
先在src目录下创建一个文件夹--plugin
-
在plugin里再创建一个文件夹-myStore
-
在myStore里,先创建一个store,js文件,在里面定义Store类
3.1 constructor
constructor({ state, getters, mutations, actions }) { this.state = Vue.observable(state || {}) this.getters = {} this.mutations = mutations || {} this.actions = actions || {} const _getters = getters || {} for (let fn in _getters) { Object.defineProperty(this.getters, fn, { get() { return _getters[fn].call(_self, _self.state) }, }) } }在constructor里,把传递进来的参数保存起来
-
用Vue.observable 来创建一个响应式对象。(这点比较重要,vuex里的数据改变了,视图要跟着刷新,主要是用这个响应式对象来处理,即vue的双向数据绑定)
-
用Object.defienProperty 来定义getters里的属性 访问器
达到的效果是,访问getters里的属性,相当于执行getters里属性同名的方法,我们在vue里面就可以直接访问属性,而不是调用方法
this.$store.getters.age
3.2 commit 调用mutations的方法
commit(cb, ...args) { _self.mutations[cb].apply(_self, [_self.state, ...args]) }3.3 dispath 调用actions的方法
dispath(cb, ...args) { _self.actions[cb].apply(_self, [_self, ...args]) }3.4 install
Vue.use() 会调用插件暴露出来的install方法
static install(_Vue, storeName = "store") { Vue = _Vue Vue.mixin({ beforeCreate() { let store = this.$options[storeName] if (store) { Vue.prototype[`$${storeName}`] = store _self = store } }, }) }install里主要是用Vue.mixin在beforeCreate里混入一些逻辑。从$options里拿出store,然后挂载到Vue的原型上。这样每个Vue实例都可以访问到store
我这里是直接导出这个Store类,所以把install放到类方法里。但是不一定要这么做,只要在导出的对象里,有一个install方法即可
export default Store -
-
在myStore里创建一个index.js文件,定义自己store里的数据
4.1 引入Vue,引入刚定义Store,通过Vue.ues 注册这个插件
import Vue from "vue" import Store from "./store" Vue.use(Store, "myStore")4.2 new一个Store实例,定义自己的数据,然后导出
export default new Store({ state: { number: 1, }, mutations: { addNumber(state) { state.number++ }, subNumber(state) { state.number-- }, }, actions: { addNumberAsync(context) { context.commit("addNumber") }, }, getters: { getNumber(state) { return state.number + 10 }, }, })
引入store
在main.js里导入我们刚才导出的store实例,然后挂载到根实例上
import myStore from "./plugin/myStore/index"
new Vue({
myStore,
render: (h) => h(App),
}).$mount("#app")
这里挂载到根实例,是为了在install方法里,从$options里拿到这个store实例,从而完成数据传递
使用store
引入store后,接下来就可以在组件里面使用store里。和我们使用vuex时,使用的方式一样,因为定义的属性和方法都是一样的
<template>
<div>
<div>{{ $myStore.state.number }}</div>
<button @click="add">增加</button>
<button @click="sub">减少</button>
<button @click="addAsync">异步增加</button>
<button @click="clickGetters">获取getters</button>
</div>
</template>
<script>
export default {
methods: {
add() {
this.$myStore.commit('addNumber')
},
sub() {
this.$myStore.commit('subNumber')
},
addAsync() {
this.$myStore.dispath('addNumberAsync')
},
clickGetters() {
console.log(this.$myStore.getters.getNumber)
},
},
}
</script>
思维导图
小结
至此,整个vuex基本实现了,不过是一个简易版的vuex,有些细节没有实现。但还是完成了