如何手写一个简易的 Vuex
前言
本文适合使用过Vuex的人阅读,来了解下怎么自己实现一个Vuex。
基本骨架
这是本项目的src/store/index.js文件,看看一般vuex的使用
importVuefrom'vue' importVuexfrom'./myvuex'//引入自己写的vuex import*asgettersfrom'./getters' import*asactionsfrom'./actions' importstatefrom'./state' importmutationsfrom'./mutations' Vue.use(Vuex)//Vue.use(plugin)方法使用vuex插件 //vuex导出一个类叫Store,并传入对象作为参数 exportdefaultnewVuex.Store({ state, mutations, actions, getters, })
Vue.use的用法:
安装Vue.js插件。如果插件是一个对象,必须提供install方法。如果插件是一个函数,它会被作为install方法。install方法调用时,会将Vue作为参数传入。这个方法的第一个参数是Vue构造器,第二个参数是一个可选的选项对象。
- 该方法需要在调用newVue()之前被调用。
- 当install方法被同一个插件多次调用,插件将只会被安装一次。
即是我们需要在./myvuex.js中导出install方法,同时导出一个类Store,于是第一步可以写出代码:
letVue=null classStore{ constructor(options){} } functioninstall(_Vue){ Vue=_Vue//上面Store类需要能获取到Vue } exportdefault{ Store, install, }
install方法
当我们使用vuex的时候,每一个组件上面都有一个this.$store属性,里面包含了state,mutations,actions,getters等,所以我们也需要在每个组件上都挂载一个$store属性,要让每一个组件都能获取到,这里我们使用Vue.mixin(mixin),用法介绍如下:
全局注册一个混入,影响注册之后所有创建的每个Vue实例。可以使用混入向组件注入自定义的行为,它将影响每一个之后创建的Vue实例。
functioninstall(_Vue){ Vue=_Vue//install方法调用时,会将Vue作为参数传入(上面Store类需要用到Vue) //实现每一个组件,都能通过this调用$store Vue.mixin({ beforeCreate(){ //通过this.$options可以获取newVue({参数})传递的参数 if(this.$options&&this.$options.store){ //证明这个this是根实例,也就是newVue产生的那个实例 this.$store=this.$options.store }elseif(this.$parent&&this.$parent.$store){ //子组件获取父组件的$store属性 this.$store=this.$parent.$store } }, }) }
state
由于Vuex是基于Vue的响应式原理基础,所以我们要让数据改变可刷新视图,则需要创建一个vue实例
classStore{ //options即是Vuex.Store({})传入的参数 constructor(options){ //vuex的核心就是借用了vue的实例,因为vue的实例数据变化,会刷新视图 letvm=newVue({ data:{ state:options.state, }, }) //state this.state=vm.state } }
commit
我们使用vuex改变数据时,是触发commit方法,即是这样使用的:
this.$store.commit('eventName','参数');
所以我们要实现一个commit方法,把Store构造函数传入的mutations做下处理
classStore{ constructor(options){ //实现state... //mutations this.mutations={}//存储传进来的mutations letmutations=options.mutations||{} //循环取出事件名进行处理(mutations[事件名]:执行方法) Object.keys(mutations).forEach(key=>{ this.mutations[key]=params=>{ mutations[key].call(this,this.state,params)//修正this指向 } }) } commit=(key,params)=>{ //key为要触发的事件名 this.mutations[key](params) } }
dispatch
跟上面的commit流程同理
classStore{ constructor(options={}){ //... //actions this.actions={} letactions=options.actions||{} Object.keys(actions).forEach(key=>{ this.actions[key]=params=>{ actions[key].call(this,this,params) } }) } dispatch=(type,payload)=>{ this.actions[type](payload) } }
getters
getters实际就是返回state的值,在使用的时候是放在computed属性,每一个getter都是函数形式;
getters是需要双向绑定的。但不需要双向绑定所有的getters,只需要绑定项目中事件使用的getters。
这里使用Object.defineProperty()方法,它会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
classStore{ constructor(options={}){ //... //getters this.getters={} letgetters=options.getters||{} Object.keys(getters).forEach(key=>{ Object.defineProperty(this.getters,key,{ get:()=>{ returngetters[key].call(this,this.state) }, }) }) } }
到此为止,已经可以使用我们自己写的vuex做一些基本操作了,但只能通过this.$store.xx的形式调用,故需要再实现方法。
map辅助函数
先来说说mapState
没有map辅助函数之前这样使用:
computed:{ count(){ returnthis.$store.state.count } }
当映射的计算属性的名称与state的子节点名称相同时,给mapState传一个字符串数组。
computed:{ //使用对象展开运算符将此对象混入到外部对象中 ...mapState(['count']) }
我们这里简单就只实现数组的情况
exportconstmapState=args=>{ letobj={} args.forEach(item=>{ obj[item]=function(){ returnthis.$store.state[item] } }) returnobj }
之后几个map辅助函数都是类似
- mapGetters
exportconstmapGetters=args=>{ letobj={} args.forEach(item=>{ obj[item]=function(){ returnthis.$store.getters[item] } }) returnobj }
- mapMutations
exportconstmapMutations=args=>{ letobj={} args.forEach(item=>{ obj[item]=function(params){ returnthis.$store.commit(item,params) } }) returnobj }
- mapActions
exportconstmapActions=args=>{ letobj={} args.forEach(item=>{ obj[item]=function(payload){ returnthis.$store.dispatch(item,payload) } }) returnobj }
完整代码
letVue=null classStore{ constructor(options){ //vuex的核心就是借用了vue的实例,因为vue的实例数据变化,会刷新视图 letvm=newVue({ data:{ state:options.state, }, }) //state this.state=vm.state //mutations this.mutations={}//存储传进来的mutations letmutations=options.mutations||{} Object.keys(mutations).forEach(key=>{ this.mutations[key]=params=>{ mutations[key].call(this,this.state,params) } }) //actions this.actions={} letactions=options.actions||{} Object.keys(actions).forEach(key=>{ this.actions[key]=params=>{ actions[key].call(this,this,params) } }) //getters this.getters={} letgetters=options.getters||{} Object.keys(getters).forEach(key=>{ Object.defineProperty(this.getters,key,{ get:()=>{ returngetters[key].call(this,this.state) }, }) }) } commit=(key,params)=>{ this.mutations[key](params) } dispatch=(type,payload)=>{ this.actions[type](payload) } } exportconstmapState=args=>{ letobj={} args.forEach(item=>{ obj[item]=function(){ returnthis.$store.state[item] } }) returnobj } exportconstmapGetters=args=>{ letobj={} args.forEach(item=>{ obj[item]=function(){ returnthis.$store.getters[item] } }) returnobj } exportconstmapMutations=args=>{ letobj={} args.forEach(item=>{ obj[item]=function(params){ returnthis.$store.commit(item,params) } }) returnobj } exportconstmapActions=args=>{ letobj={} args.forEach(item=>{ obj[item]=function(payload){ returnthis.$store.dispatch(item,payload) } }) returnobj } functioninstall(_Vue){ Vue=_Vue//install方法调用时,会将Vue作为参数传入(上面Store类需要用到Vue) //实现每一个组件,都能通过this调用$store Vue.mixin({ beforeCreate(){ //通过this.$options可以获取newVue({参数})传递的参数 if(this.$options&&this.$options.store){ //证明这个this是根实例,也就是newVue产生的那个实例 this.$store=this.$options.store }elseif(this.$parent&&this.$parent.$store){ //子组件获取父组件的$store属性 this.$store=this.$parent.$store } }, }) } exportdefault{ Store, install, }
以上就是如何手写一个简易的Vuex的详细内容,更多关于手写vuex的资料请关注毛票票其它相关文章!