Vuex实现原理解析
Vuex基本原理解析
下面将从Vuex的安装到实现数据存取的过程进行讲解。建议下载一套Vuex源码进行对比学习。
安装过程
在Vuex初始化的过程中,首先会经过安装操作,通过vue.use方法进行install安装,安装之前会对当前环境进行判断是否已经安装过Vuex,以避免重复安装。
//store.js中安装方法
if (!Vue && typeof window !== 'undefined' && window.Vue) {
install(window.Vue)
}
...
...
export function install (_Vue) {
if (Vue && _Vue === Vue) {
if (process.env.NODE_ENV !== 'production') {
console.error(
'[vuex] already installed. Vue.use(Vuex) should be called only once.'
)
}
return
}
Vue = _Vue
applyMixin(Vue)
}
最后调用applyMixin方法将vuexInit(Vuex初始化的钩子函数)方法(存在mixin.js中)混入Vue组件的beforeCreate钩子中。其中vuexInit方法中对传入的store进行处理,以保证所有组件都共用同一份store。那么此时我们会想到,Vuex就是为了解决组件之间的数据共享问题,通过这种方式,可以实现每个组件访问的都是同一个数据仓库(store),接着往下看。
function vuexInit () {
const options = this.$options
// store injection
if (options.store) {
/*存在store其实代表的就是Root节点,直接执行store(function时)或者使用store(非function)*/
this.$store = typeof options.store === 'function'
? options.store()
: options.store
} else if (options.parent && options.parent.$store) {
/*子组件直接从父组件中获取$store,这样就保证了所有组件都公用了全局的同一份store*/
this.$store = options.parent.$store
}
}
接下来在源码store.js中的constructor中,大部分逻辑是对定义一些下面需要用到的数组对象、是否为严格模式、以及定义用来实现watch的vue实例等,我们可以暂时不用管。主要是看下面两个函数:
// init root module.
// this also recursively registers all sub-modules
// and collects all module getters inside this._wrappedGetters
/*初始化根module,这也同时递归注册了所有子modle,收集所有module的getter到_wrappedGetters中去,this._modules.root代表根module才独有保存的Module对象*/
installModule(this, state, [], this._modules.root)
// initialize the store vm, which is responsible for the reactivity
// (also registers _wrappedGetters as computed properties)
/* 通过vm重设store,新建Vue对象使用Vue内部的响应式实现注册state以及computed */
resetStoreVM(this, state)
第一种方法是安装模块,主要通过命名空间识别和创建局部上下文来管理和响应子模块。
第二种方法是注册state并绑定getter。.defineProperty实现每个getter的get操作,比如获取thisseproperty.$store.getters.storest时获得的是storest._vm.test方法,返回定义后的方法给computed属性,为实现Vuex响应式做准备。
forEachValue(wrappedGetters, (fn, key) => {
// use computed to leverage its lazy-caching mechanism
computed[key] = () => fn(store)
Object.defineProperty(store.getters, key, {
get: () => store._vm[key],
enumerable: true // for local getters
})
})
接下来这段代码,通过new一个Vue对象,借助Vue内部的响应式原理实现注册state以及computed,来实现Vuex的响应式操作。好了,那么文中一开始提到的第一个问题已经回答了!
store._vm = new Vue({
data: {
$$state: state
},
computed
})
第二问题:为什么更改state状态只能通过提交mutation的方式才能实现修改?
非严格模式下直接使用this.store.state.xxx修改state不会发生改变, 并且控制台不会发生报错。而使用严格模式下面会直接进行报错,原因在于使用严格模式的时候会检测store._committing(在Store的constructor中有定义)状态,当store._committing的值为false,表示不是通过mutation的方法修改的,你可以通过控制台直接操作store中的state数据,手动修改它,并打印此时store._committing的值。当为严格模式的时候,执行下面代码:
// enable strict mode for new vm
/* 使用严格模式,保证修改store只能通过mutation */
if (store.strict) {
enableStrictMode(store)
}
function enableStrictMode (store) {
store._vm.$watch(function () { return this._data.$$state }, () => {
if (process.env.NODE_ENV !== 'production') {
/* 检测store中的_committing的值,如果是false代表不是通过mutation的方法修改的 */
assert(store._committing, `Do not mutate vuex store state outside mutation handlers.`)
}
}, { deep: true, sync: true })
}
所以说Vuex更改state状态是必须要通过显示提交mutation的方式,当调用commit()提交一个mutation的时候,store._committing的值就变为true。
更多推荐

所有评论(0)