vuex集中式存储管理应用所有组件的状态,并以响应的规则保证状态以可预测的方式 发生变化。
步骤:
1.Store类,保存选项,_mutations,_actions,getters
2.响应式状态:new Vue方式设置响应式。
3.get state 和 set state commit dispatch
4.install方法 挂载store到vue的原型对象上,所有实例都可以读取到。
生成Vuex类
说到底,vuex也还是个插件,所以生成一个类,按照使用插件的形式将其export导出。
挂载到Vue实例上我们还是使用mixin的方式,原理和vue-router的自定义方式一致,这里就不多说了。
let Vueclass Store{constructor(options){//保存选项// state为响应式数据this.state = new Vue({data:options.state})}
}function install(_Vue){Vue = _Vue//挂载$store给外面使用Vue.mixin({beforeCreate() {if(this.$options.store){//挂载到vue原型上,每个vue实例都可以访问到Vue.prototype.$store = this.$options.store}},})
}export default {Store,install
}
但是在这里,我们使用new Vue的data属性来让state成为一个响应式的数据,有个弊端就是能够直接的修改data里的数据,那这样违反了vuex单向数据流的特点,那么我们要封装一下。
官网解释,有两个的$变量就不做代理,所以也就不会再_vm的根上访问到$$state这个属性,同时设置set get俩属性,get为只读,那么外面就无法修改state的值,只能通过commit或actions进行修改。_vm下的响应式对象会挂载在_data的对象上,_vm下的$data是一个原始对象,不具备响应式,所以使用_data。
如下图,$data不存在observer
class Store{constructor(options){//保存选项// state为响应式数据this._vm = new Vue({data:{$$state:options.state}})}//给用户暴露接口get state(){console.log(this._vm);return this._vm._data.$$state}set state(val){throw Error('replaceSate')}
}
commit和dispatch
响应式的数据发生变化就会引起render函数渲染数据 ,在commit中会接收到参数,我们在构造函数中存储传入的mutations和actions,然后在commit和dispach中匹配到详情的操作,然后执行。
但是在执行过程中有坑,this的指向问题:当前的调用是在外部,所以指向的是外部的store实例,是没有commit等参数,所以没有办法调用到。需要在constructor存储上下文,并且改变this指向到当前的实例,获取到对应的上下文即可。
class Store{constructor(options){//保存选项this._mutations = options.mutations||{}this._actions = options.actions||{}// state为响应式数据this._vm = new Vue({data:{$$state:options.state}})//上下文的绑定this.commit=this.commit.bind(this)this.dispatch=this.dispatch.bind(this)}//给用户暴露接口get state(){return this._vm._data.$$state}set state(val){throw Error('replaceSate')}//store.commit(type,payload)commit(type,payload){//获取mutitionsconst entry = this._mutations[type]if(!entry){console.error('unknown mutition type');}entry(this.state,payload)}dispatch(type,payload){const entry = this._actions[type]if(!entry){console.error('unknown actions type');}console.log(this);entry(this,payload)//注意这里的this指向问题,当前的调用是在外部,所以指向的是外部的store实例,是没有commit等参数,所以没有办法调用到。需要在constructor存储上下文,并且改变this指向。}
}
getters
getters我们可以借助computed属性,只可获取不可更改,获取到getters的key给到computed,并且在给一个函数,在其内部调用fn并且传入state,再将computed属性进行响应式处理。
this._wrappedGetters = options.getters||{}//定义computed选项const computed={}this.getters={}const store= thisObject.keys(this._wrappedGetters).forEach(key=>{//获取用户定义的getterconst fn = store._wrappedGetters[key]// 转换为computed可以使用的无参数形式computed[key]=function(){return fn(store.state)}//为getters定义只读属性Object.defineProperty(store.getters,key,{get:()=>{return store._vm[key]}})})// state为响应式数据this._vm = new Vue({data:{$$state:options.state},computed})
附完整代码:
let Vueclass Store{constructor(options){//保存选项this._mutations = options.mutations||{}this._actions = options.actions||{}this._wrappedGetters = options.getters||{}//定义computed选项const computed={}this.getters={}const store= thisObject.keys(this._wrappedGetters).forEach(key=>{//获取用户定义的getterconst fn = store._wrappedGetters[key]// 转换为computed可以使用的无参数形式computed[key]=function(){return fn(store.state)}//为getters定义只读属性Object.defineProperty(store.getters,key,{get:()=>{return store._vm[key]}})})// state为响应式数据this._vm = new Vue({data:{$$state:options.state},computed})//上下文的绑定this.commit=this.commit.bind(this)this.dispatch=this.dispatch.bind(this)}//给用户暴露接口get state(){return this._vm._data.$$state}set state(val){throw Error('replaceSate')}//store.commit(type,payload)commit(type,payload){//获取mutitionsconst entry = this._mutations[type]if(!entry){console.error('unknown mutition type');}entry(this.state,payload)}dispatch(type,payload){const entry = this._actions[type]if(!entry){console.error('unknown actions type');}entry(this,payload)//注意这里的this指向问题,当前的调用是在外部,所以指向的是外部的store实例,是没有commit等参数,所以没有办法调用到。需要在constructor存储上下文,并且改变this指向。}
}function install(_Vue){Vue = _Vue//挂载$store给外面使用Vue.mixin({beforeCreate() {if(this.$options.store){//挂载到vue原型上,每个vue实例都可以访问到Vue.prototype.$store = this.$options.store}},})
}export default {Store,install
}