定义
Vuex的出现是主要是为了组件层级嵌套过深时的组件通信问题。采用中心化的思想,集中管理需要所有组件的状态的一个仓库,并定义一套规范保证状态以往可预测的方向发生改变。本质上是用一个对象存储组件的状态,利用Vue的data函数里的数据是响应式的,使用Vue.mixin()方法,在data数据进行初始化前,将store注入到每个Vue实例中,然后在初始化的时候实现了响应式,在视图与数据之间的连接。定义了两种行为,Actions 用来处理异步状态变更(内部还是调用 Mutations),Mutations 处理同步的状态变更,整个链路应该是一个闭环,单向的,完美契合 FLUX 的思想。
Vuex注册
组件注册使用Vue.use(Vuex),需提供install方法
/**
* store.js - store 注册
*/
let Vue
export function install(_Vue) {
// 拿到 Vue 的构造器,存起来
Vue = _Vue
// 通过 mixin 注入到每一个vue实例 👉 https://cn.vuejs.org/v2/guide/mixins.html
Vue.mixin({ beforeCreate: vuexInit })
function vuexInit () {
const options = this.$options
// 这样就可以通过 this.$store 访问到 Vuex 实例,拿到 store 了
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.js - 利用 VUE 实现响应式
*/
// this.$store.state
export class Store {
constructor(options = {}) {
resetStoreVM(this, options.state)
}
get state () {
return this._vm._data.$$state
}
}
function resetStoreVM(store, state) {
// 因为 vue 实例的 data 是响应式的,正好利用这一点,就可以实现 state 的响应式
store._vm = new Vue({
data: {
$$state: state
}
})
}
衍生数据(getters)
/**
* store.js - 衍生数据(getters)
*/
export class Store {
constructor(options = {}) {
const state = options.state
resetStoreVM(this, state)
// 我们用 getters 来收集衍生数据 computed
this.getters = {}
_.forEach(this.getters, (name, getterFn) => {
Object.defineProperty(this.getters, name, {
get: () => getterFn(this.state)
})
})
}
get state () {
return this._vm._data.$$state
}
}
function resetStoreVM(store, state) {
store._vm = new Vue({
data: {
$$state: state
}
})
}
Actions/Mutations
/**
* store.js - Actions/Mutations 行为改变数据
*/
export class Store {
constructor(options = {}) {
const state = options.state
resetStoreVM(this, state)
this.getters = {}
_.forEach(options.getters, (name, getterFn) => {
Object.defineProperty(this.getters, name, {
get: () => getterFn(this.state)
})
})
// 定义的行为,分别对应异步和同步行为处理
this.actions = {}
this.mutations = {}
_.forEach(options.mutations, (name, mutation) => {
this.mutations[name] = payload => {
// 最终执行的就是 this._vm_data.$$state.xxx = xxx 这种操作
mutation(this.state, payload)
}
})
_.forEach(options.actions, (name, action) => {
this.actions[name] = payload => {
// action 专注于处理异步,这里传入 this,这样就可以在异步里面通过 commit 触发 mutation 同步数据变化了
action(this, payload)
}
})
}
// 触发 mutation 的方式固定是 commit
commit(type, payload) {
this.mutations[type](payload)
}
// 触发 action 的方式固定是 dispatch
dispatch(type, payload) {
this.actions[type](payload)
}
get state () {
return this._vm._data.$$state
}
}
function resetStoreVM(store, state) {
store._vm = new Vue({
data: {
$$state: state
}
})
}
拆分出多个module
// module 可以对状态模型进行分层,每个 module 又含有自己的 state、getters、actions 等
// 定义一个 module 基类
class Module {
constructor(rawModule) {
this.state = rawModule || {}
this._rawModule = rawModule
this._children = {}
}
getChild (key) {
return this._children[key]
}
addChild (key, module) {
this._children[key] = module
}
}
// module-collection.js 把 module 收集起来
class ModuleCollection {
constructor(options = {}) {
this.register([], options)
}
register(path, rawModule) {
const newModule = new Module(rawModule)
if (path.length === 0 ) {
// 如果是根模块 将这个模块挂在到根实例上
this.root = newModule
}
else {
const parent = path.slice(0, -1).reduce((module, key) => {
return module.getChild(key)
}, this.root)
parent.addChild(path[path.length - 1], newModule)
}
// 如果有 modules,开始递归注册一波
if (rawModule.modules) {
_.forEach(rawModule.modules, (key, rawChildModule) => {
this.register(path.concat(key), rawChildModule)
})
}
}
}
// store.js 中
export class Store {
constructor(options = {}) {
// 其余代码...
// 所有的 modules 注册进来
this._modules = new ModuleCollection(options)
// 但是这些 modules 中的 actions, mutations, getters 都没有注册,所以我们原来的方法要重新写一下
// 递归的去注册一下就行了,这里抽离一个方法出来实现
installModule(this, this.state, [], this._modules.root);
}
}
function installModule(store, state, path, root) {
// getters
const getters = root._rawModule.getters
if (getters) {
_.forEach(getters, (name, getterFn) => {
Object.defineProperty(store.getters, name, {
get: () => getterFn(root.state)
})
})
}
// mutations
const mutations = root._rawModule.mutations
if (mutations) {
_.forEach(mutations, (name, mutation) => {
let _mutations = store.mutations[name] || (store.mutations[name] = [])
_mutations.push(payload => {
mutation(root.state, payload)
})
store.mutations[name] = _mutations
})
}
// actions
const actions = root._rawModule.actions
if (actions) {
_.forEach(actions, (name, action) => {
let _actions = store.actions[name] || (store.actions[name] = [])
_actions.push(payload => {
action(store, payload)
})
store.actions[name] = _actions
})
}
// 递归
_.forEach(root._children, (name, childModule) => {
installModule(this, this.state, path.concat(name), childModule)
})
}
以上只是以最简化的代码实现了 vuex 核心的 state module actions mutations getters 机制,如果对源代码感兴趣,可以看看这篇文章