写在前面
Vuex用了那么久了,一直有思考是如何工作的,
今天从 使用层面和原理实现 探究 Vuex 如何实现的 状态管理,
基本使用
- 使用 Vuex 插件
// 1.使用Vuex插件
Vue.use(Vuex);
- 创建 store 实例
// 2.创建store实例
new Vuex.Store({
state: {
counter: 0,
},
mutations: {
add(state) {
state.counter++;
},
},
actions: {
add({ commit }) {
setTimeout(() => {
commit('add');
}, 1000);
},
},
getters: {
doubleCounter(state) {
return state.counter * 2;
},
},
});
- 配置到 options 中
// 3.配置到options中
new Vue({
// ...其他options
router,
render: h => h(App),
}).$mount('#app');
问题
- 为什么要先注册插件:
Vue.use(Vuex),注册插件都做了那些事情? new Vuex.Store(...)做了哪些事情,为什么 state 可以响应式变化?mutations参数中的state从哪里来actions参数中解构出了commit,commit是什么,从哪里来?
原理探究
state如何做到响应式
在研究VueRouter源码时专门深入了解了用Vue做响应式的几种方式
store._vm = new Vue({
data: {
$$state: state,
},
});
Vue响应式模块对于$$开头的属性,不代理到Vue上面,因此 vue 实例 不能直接访问到$$开头的属性,起到了保护变量的作用,只允许通过actions和mutations来更新state
getters 的 原理
getters在使用的时候,是不是就已经发现和 Vue 的computed很像了,没错,Vuex内部对于getters的实现同样是借助了computed,
而computed为何物?其实只是把我们写的computed函数经过简单包装后赋值给Object.defineProperty中第三个参数的get方法,
源码中的操作也很简单,将getters简单处理后,直接赋值给new Vue的computed属性即可
核心代码其实就是这样
const computed = {};
Object.keys(this.getters).forEach(key => {
const fn = this.getters[key];
computed[key] = () => fn(this.state);
// 将getters中属性的获取代理到vue
Object.defineProperty(this.getters, key, {
get: () => this._vm[key],
});
});
store._vm = new Vue({
// ... state: {$$state: state}
computed,
});
mutation 和 actions
这部分没有太过于复杂
核心实现如下
commit(type, payload) {
const entry = this._mutations[type];
if (!entry) {
console.error('unkown mutation type');
}
entry(this.state, payload);
}
dispatch(type, payload) {
const entry = this._actions[type];
if (!entry) {
console.error('unkown action type');
}
entry(this, payload);
}
思考与总结
-
我对于Vuex的理解
通过源码,我所理解的Vuex,实际上也是一个全局都可以访问的
Vue实例,
区别是- 这个实例的 响应式
data要通过state访问, - 其修改方式变为 actions 和 mutations
- 这个实例的 响应式
-
store中的数据可以直接被修改吗?可以,并且还是响应式变化,但项目变大时将变得很难维护,无法做变化监测、日志记录、状态持久化等中间件的开发
-
actions接收的第一个参数第一个参数是
store上下文,可以获取到commit,dispatch,state,getters所有内容 -
Vue的data中$$XX和XX的区别$$XX将不会被Vue代理到this,直接通过this.$$XX无法获取,但是可以通过_data获取,这部分会在 Vue源码 中详细探究