前言
Vue 本身具备响应式的状态,组件间也可以通过 props/emit 等方式进行通信,但是,当页面达到一定的复杂程度,多个视图共享同一状态,且当多个视图发出的行为需要变更状态时,单向数据流的简洁性就变得容易被破坏,难以维护。
Vuex 是专门为 Vue 应用程序开发的状态管理模式,是 Flux 架构的一种实现,将状态抽离出来作为 Store 层,当 View 层发起修改,会要求 Store 进行变更,变更之后再通知 View 更新视图,这样组件就可以从繁琐的组件通信解脱出来,只需关注视图层的逻辑。
工作原理
为什么 vuex 只能用于 vue,因为 vuex 本质是一个 vue 插件,依赖于 vue 的响应式系统,使用 vuex 的第一步是通过 Vue.use(Vuex)
应用 vuex,查看源码,有对外暴露 install
方法
export function install (_Vue) {
if (Vue && _Vue === Vue) return
Vue = _Vue
applyMixin(Vue)
}
复制代码
当应用 vuex 时,会调用 applyMixin
方法,下面是核心代码,通过 Vue.mixin
注册一个全局 mixin,在所有组件的 beforeCreate
阶段,都会调用 vuexInit
,向组件实例注入 store
new Vue
需要传入 store 实例,根实例执行 vuexInit 时,可以从 options 获取到 store,然后保存在 $store
属性,由于父组件先于子组件执行 beforeCreate
,因此子组件执行 vuexInit 时总能够获取到父组件的 $store
,然后保存起来,这也是为什么我们可以在任意组件直接通过 this.$store
获取到 store 实例。
export default function (Vue) {
Vue.mixin({ beforeCreate: vuexInit })
function vuexInit () {
const options = this.$options
// store injection
if (options.store) {
this.$store = typeof options.store === 'function'
? options.store()
: options.store
} else if (options.parent && options.parent.$store) {
this.$store = options.parent.$store
}
}
}
复制代码
核心原理
store 实例通过 new Vuex.Store
创建,由此可见 vuex 对外暴露一个 Store 类,下面来看核心模块的实现原理
State
在 vue 中,我们通过 store.state
获取 state,从下面代码可知,store.state
实际上是 this._vm._data.$$state
的代理
export class Store {
constructor (options = {}) {
this._modules = new ModuleCollection(options)
const state = this._modules.root.state
resetStoreVM(this, state)
}
get state () {
return this._vm._data.$$state
}
set state (v) {
if (__DEV__) {
assert(false, `use store.replaceState() to explicit replace store state.`)
}
}
}
复制代码
创建 Store 实例会调用 resetStoreVM(this, state)
,下面是核心代码,可以看到 store 内部创建了一个 vue 实例,由此可见,store 内部新建了一个没有 template 的 vue 实例,而 state 就是该实例的响应式属性 $state
,对 state 的修改,实际上就跟在普通组件中修改状态没有区别。
function resetStoreVM (store, state, hot) {
store.getters = {}
const wrappedGetters = store._wrappedGetters
const computed = {}
forEachValue(wrappedGetters, (fn, key) => {
computed[key] = partial(fn, store)
Object.defineProperty(store.getters, key, {
get: () => store._vm[key],
enumerable: true // for local getters
})
})
store._vm = new Vue({
data: {
$$state: state
},
computed
})
}
复制代码
Getters
由上面代码可知,getters 实际上是 computed
小结
Vuex 是一个 vue 插件,非常轻量,这得益于 vue 的响应式系统,Store 内部维护了一个 Vue 实例对象,state/getters 本质是 Vue 实例的 data 和 computed,