Vuex 实现原理深度解析
Vuex 作为 Vue 的官方状态管理库,其内部实现非常精妙。下面我将从核心机制、响应式原理、模块系统等方面深入剖析 Vuex 的实现原理。
一、Vuex 核心架构
1. 整体架构图
┌───────────────────────────────────────────────────┐
│ Vue Components │
└───────────────┬───────────────────┬───────────────┘
│ │
↓ ↓
┌───────────────┴───┐ ┌────────┴─────────────┐
│ Actions │ │ Mutations │
└───────────────┬───┘ └────────┬─────────────┘
│ │
└─────────┬─────────┘
↓
┌───────────────────────────────────────────────────┐
│ State │
└───────────────────────────────────────────────────┘
2. 核心实现机制
- Store 构造函数:创建全局唯一的 store 实例
- 响应式 state:利用 Vue 的响应式系统
- 提交/分发机制:实现 mutations 和 actions
- 模块收集系统:处理嵌套模块
二、核心源码解析
1. Store 构造函数
class Store {
constructor(options = {}) {
// 初始化核心属性
this._committing = false
this._actions = Object.create(null)
this._mutations = Object.create(null)
this._wrappedGetters = Object.create(null)
this._modules = new ModuleCollection(options)
// 绑定 commit 和 dispatch 的上下文
const store = this
const { dispatch, commit } = this
this.dispatch = function boundDispatch(type, payload) {
return dispatch.call(store, type, payload)
}
this.commit = function boundCommit(type, payload, options) {
return commit.call(store, type, payload, options)
}
// 初始化根模块
installModule(this, this.state, [], this._modules.root)
// 使用 Vue 实例使 state 响应式
resetStoreVM(this, this.state)
}
}
2. 响应式 state 实现
Vuex 使用 Vue 实例来使 state 变为响应式:
function resetStoreVM(store, state) {
// 旧的 Vue 实例
const oldVm = store._vm
// 创建新的 Vue 实例
store._vm = new Vue({
data: {
$$state: state
},
computed: store.getters
})
// 销毁旧的 Vue 实例
if (oldVm) {
Vue.nextTick(() => oldVm.$destroy())
}
}
这样做的巧妙之处在于:
- 利用 Vue 已有的响应式系统
- getters 作为 computed 属性实现缓存
- 状态变化自动触发视图更新
3. commit 实现(同步修改 state)
commit(_type, _payload, _options) {
// 参数标准化处理
const { type, payload, options } = unifyObjectStyle(_type, _payload, _options)
const mutation = { type, payload }
const entry = this._mutations[type]
// 执行所有同名的 mutation
this._withCommit(() => {
entry.forEach(function commitIterator(handler) {
handler(payload)
})
})
// 通知订阅者
this._subscribers.forEach(sub => sub(mutation, this.state))
}
_withCommit(fn) {
const committing = this._committing
this._committing = true
fn()
this._committing = committing
}
关键点:
_withCommit确保只有通过 mutation 才能修改 state- 严格模式下会检查
_committing标志位
4. dispatch 实现(异步操作)
dispatch(_type, _payload) {
// 参数标准化处理
const { type, payload } = unifyObjectStyle(_type, _payload)
const entry = this._actions[type]
// 执行所有同名的 action
return entry.length > 1
? Promise.all(entry.map(handler => handler(payload)))
: entry[0](payload)
}
特点:
- 返回 Promise 便于异步处理
- 支持多个同名 action 并行执行
5. 模块系统实现
Vuex 使用 ModuleCollection 类管理模块:
class ModuleCollection {
constructor(rawRootModule) {
this.register([], rawRootModule)
}
register(path, rawModule) {
const newModule = new Module(rawModule)
if (path.length === 0) {
this.root = newModule
} else {
const parent = this.get(path.slice(0, -1))
parent.addChild(path[path.length - 1], newModule)
}
// 递归注册嵌套模块
if (rawModule.modules) {
Object.keys(rawModule.modules).forEach(key => {
this.register(path.concat(key), rawModule.modules[key])
})
}
}
}
模块安装过程:
function installModule(store, rootState, path, module) {
// 设置模块 state
if (path.length > 0) {
const parentState = getNestedState(rootState, path.slice(0, -1))
Vue.set(parentState, path[path.length - 1], module.state)
}
// 注册 mutation
module.forEachMutation((mutation, key) => {
const namespacedType = namespace + key
registerMutation(store, namespacedType, mutation, local)
})
// 注册 action
module.forEachAction((action, key) => {
const namespacedType = namespace + key
registerAction(store, namespacedType, action, local)
})
// 注册 getter
module.forEachGetter((getter, key) => {
const namespacedType = namespace + key
registerGetter(store, namespacedType, getter, local)
})
// 递归安装子模块
module.forEachChild((child, key) => {
installModule(store, rootState, path.concat(key), child)
})
}
三、响应式原理详解
1. State 响应式实现
Vuex 通过将 state 作为 Vue 实例的 data 属性实现响应式:
store._vm = new Vue({
data: {
$$state: state
},
computed: getters
})
$$state会被 Vue 转换为响应式属性- 访问
store.state实际上是访问store._vm._data.$$state - 修改 state 必须通过 mutation 才能触发响应式更新
2. Getters 实现机制
Getters 作为 Vue 的 computed 属性实现:
function resetStoreVM(store, state) {
const wrappedGetters = store._wrappedGetters
const computed = {}
// 将 getter 转换为 computed 属性
Object.keys(wrappedGetters).forEach(key => {
computed[key] = () => wrappedGetters[key](store)
Object.defineProperty(store.getters, key, {
get: () => store._vm[key],
enumerable: true
})
})
store._vm = new Vue({
data: { $$state: state },
computed // getters 作为 computed
})
}
优势:
- 自动缓存计算结果
- 只有当依赖的 state 变化时才重新计算
四、插件系统原理
Vuex 的插件是一个函数,接收 store 作为参数:
const myPlugin = store => {
// 订阅 store 的 mutation
store.subscribe((mutation, state) => {
console.log(mutation.type)
})
}
const store = new Vuex.Store({
// ...
plugins: [myPlugin]
})
内部实现:
class Store {
constructor() {
this._subscribers = []
// 应用插件
plugins.forEach(plugin => plugin(this))
}
subscribe(fn) {
this._subscribers.push(fn)
}
}
五、严格模式实现
严格模式下会深度 watch state,检查是否通过 mutation 修改:
function enableStrictMode(store) {
store._vm.$watch(function() {
return this._data.$$state
}, () => {
assert(store._committing,
`Do not mutate vuex store state outside mutation handlers.`)
}, { deep: true, sync: true })
}
六、辅助函数实现
以 mapState 为例:
function mapState(states) {
const res = {}
normalizeMap(states).forEach(({ key, val }) => {
res[key] = function mappedState() {
return typeof val === 'function'
? val.call(this, this.$store.state, this.$store.getters)
: this.$store.state[val]
}
})
return res
}
七、总结:Vuex 设计精妙之处
-
巧用 Vue 响应式系统:
- 复用 Vue 的响应式机制
- getters 作为 computed 属性实现缓存
-
严格的状态变更控制:
- 只能通过 commit 修改 state
- 严格模式防止意外修改
-
灵活的模块系统:
- 支持嵌套模块
- 命名空间隔离
-
可扩展的插件机制:
- 日志、持久化等插件
- 订阅 mutation 变化
-
统一的状态管理:
- 单一状态树
- 可预测的状态变化
理解 Vuex 的实现原理有助于:
- 更高效地使用 Vuex
- 能够开发 Vuex 插件
- 在遇到问题时能够快速定位
- 为学习其他状态管理库打下基础
对于现代 Vue 3 项目,虽然 Pinia 是更推荐的选择,但理解 Vuex 的设计思想仍然非常有价值。