在阅读本篇之前,希望你已经对vuex的用法有了一定了解。可以参考一下Vue统一状态管理——Vuex。
Vuex
Vuex集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以可预测的方式发生改变
整合vuex
核心概念
- state 状态、数据
- mutations 更改状态的函数
- actions 异步操作
- store 包含以上概念的容器
状态 - state
state保存应用状态
1 2 3
| export default new Vuex.Store({ state: { counter:0 }, })
|
状态变更 - mutations
mutations用于修改状态
1 2 3 4 5 6 7
| export default new Vuex.Store({ mutations: { add(state) { state.counter++ } } })
|
派生状态 - getters
从state派生出新状态,类似计算属性
1 2 3 4 5 6 7
| export default new Vuex.Store({ getters: { doubleCounter(state) { return state.counter * 2; } } })
|
动作 - actions
添加业务逻辑,类似于controller
1 2 3 4 5 6 7
| export default new Vuex.Store({ actions: { add({ commit }) { setTimeout(() => { commit('add')}, 1000); } } })
|
测试代码:
1 2 3
| <p @click="$store.commit('add')">counter: {{$store.state.counter}}</p> <p @click="$store.dispatch('add')">async counter: {{$store.state.counter}}</p> <p>double:{{$store.getters.doubleCounter}}</p>
|
Vuex原理解析
目标分析:
- 实现一个插件:声明store类,挂载$store
- Store具体实现:
- 创建响应式的state,保存mutations、actions和getters
- 实现commit根据用户传入的type,执行对应的mutation
- 实现dispatch根据用户传入的type执行对应的action,同时传递上下文
- 实现getters,按照getters定义对state做派生
初始化
Store声明、install实现,jvuex.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| let Vue class Store{ constructor(options){ this.state = new Vue({ data: options.state }) } } function install(_Vue){ Vue = _Vue Vue.mixin({ beforeCreate(){ if(this.$options.store){ Vue.prototype.$store = this.$options.store } } }) } export default { Store, install }
|
实现 commit
根据用户传入type获取并执行对应的mutation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| class Store{ constructor(options){ this._mutations = options.mutations this.state = new Vue({ data: options.state }) } commit(type,payload){ const entry = this._mutations[type] if(entry){ entry(this.state,payload) } } }
|
实现 actions
根据用户传入的type获取并执行对应的mutation
1 2 3 4 5 6 7
| dispatch(type,payload){ const entry = this._actions[type] if (entry) { entry(this, payload) } }
|
然后我们运行一下会发现报以下错误
错误原因:因为class的影响,this指向出了问题
参考了一下官方的解决方案,我们修改Store:
1 2 3 4 5 6
| constructor(options) { ...... this.commit = this.commit.bind(this) this.dispatch = this.dispatch.bind(this) }
|
优化state
目前的代码还没有对state做一个很好的保护,会存在将来用户直接去改state中的值得行为,继续修改Store的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| constructor(options) { ...... this._vm = new Vue({ data:{ $$state:options.state } }) ...... }
get state(){ console.log(this._vm) return this._vm._data.$$state } set state(v){ console.error('禁止这样修改') }
|
实现 getters
继续修改Store类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| class Store { constructor(options) { ...... this._wrapperGetters = options.getters const computed = {} this.getters = {} const store=this Object.keys(this._wrapperGetters).forEach(key=>{ const fn = store._wrapperGetters[key] computed[key]=function(){ return fn(store.state) } Object.defineProperty(store.getters,key,{ get:()=> store._vm[key] }) }) ...... } }
|