目录
大家好,我是小杨,一个在前端圈摸爬滚打6年的老司机。今天咱们不聊怎么用Vuex,而是来点更刺激的——扒一扒Vuex的工作原理。相信看完这篇,你会对Vuex有全新的认识,再也不怕面试官问你"Vuex原理"这种问题了!
1. Vuex是个啥?先看看它的"身份证"
每次我用Vuex的时候就在想,这玩意儿不就是个全局对象嘛,为啥要搞得这么复杂?后来被坑了几次才明白,它可比简单的全局对象强大多了。
简单来说,Vuex的核心就是一个响应式的store。它通过Vue的响应式系统来实现状态变更的自动更新。来看个最简单的实现:
class Store {
constructor(options) {
this._vm = new Vue({
data: {
$$state: options.state
}
})
}
get state() {
return this._vm._data.$$state
}
set state(v) {
console.error('请使用mutations修改state!')
}
}
看到没?Vuex内部其实是创建了一个Vue实例,把state挂载到data上,这样就获得了响应式能力。这就是为啥我们能实时获取状态变化。
2. Mutations:唯一能改state的"门卫"
我刚开始用Vuex时,总想直接改state,结果发现页面不更新,被坑得不要不要的。后来才明白,Vuex通过commit来修改state是有深意的。
class Store {
constructor(options) {
// ...其他代码
this._mutations = options.mutations
}
commit(type, payload) {
const entry = this._mutations[type]
if (!entry) {
console.error(`未知的mutation类型:${type}`)
return
}
entry(this.state, payload)
}
}
这样设计有两个好处:
-
所有状态变更都可追踪(配合devtools)
-
确保状态变更的同步性(这点很重要!)
3. Actions:处理异步的"跑腿小哥"
Actions是我最喜欢的功能之一,它允许我们处理异步操作。来看看它的简单实现:
class Store {
constructor(options) {
// ...其他代码
this._actions = options.actions
}
dispatch(type, payload) {
const entry = this._actions[type]
if (!entry) {
console.error(`未知的action类型:${type}`)
return
}
return entry(this, payload)
}
}
注意actions接收的是store实例本身,这样我们就能在action里commit mutations了:
actions: {
async fetchUser({ commit }) {
const user = await api.getUser()
commit('SET_USER', user)
}
}
4. Getters:计算属性的"加强版"
Getters就像是store的计算属性,它的实现也很有意思:
class Store {
constructor(options) {
// ...其他代码
this._wrappedGetters = options.getters
this.getters = {}
const computed = {}
Object.keys(this._wrappedGetters).forEach(key => {
computed[key] = () => {
return this._wrappedGetters[key](this.state)
}
Object.defineProperty(this.getters, key, {
get: () => this._vm[key],
enumerable: true
})
})
this._vm = new Vue({
data: {
$$state: options.state
},
computed
})
}
}
看到没?Getters本质上就是Vue的计算属性,所以它也有缓存特性,只有依赖的state变化时才会重新计算。
5. 模块化:Vuex的"分家术"
当项目大了,store就会变得臃肿。这时候模块化就派上用场了。Vuex的模块化实现也很巧妙:
class Module {
constructor(rawModule) {
this._children = {}
this._rawModule = rawModule
this.state = rawModule.state || {}
}
getChild(key) {
return this._children[key]
}
addChild(key, module) {
this._children[key] = module
}
}
每个模块都有自己的state、mutations等,Vuex会通过命名空间来组织它们。
6. 插件系统:Vuex的"可扩展性"
Vuex的插件机制允许我们在store的生命周期中注入自定义逻辑。比如官方提供的logger插件:
function createLogger() {
return store => {
store.subscribe((mutation, state) => {
console.log('mutation:', mutation.type)
console.log('payload:', mutation.payload)
console.log('state after:', state)
})
}
}
插件其实就是一个函数,接收store参数,然后可以订阅mutation的变化。
7. 我踩过的坑
记得有一次我写了个插件,想在mutation之后做一些处理,结果发现状态还没更新。后来才明白,subscribe的回调是在mutation执行之后,但在state更新之前调用的。正确的做法是:
store.subscribe((mutation, state) => {
this.$nextTick(() => {
// 这里state已经更新了
})
})
总结
Vuex的核心原理其实并不复杂:
-
利用Vue的响应式系统实现state的响应式
-
通过commit/dispatch来规范状态变更流程
-
模块化组织大型应用的状态
-
插件机制提供扩展能力
理解这些原理后,用起Vuex来会更加得心应手。下次面试被问到时,你也可以自信地给面试官上一课了!
⭐ 写在最后
请大家不吝赐教,在下方评论或者私信我,十分感谢🙏🙏🙏.✅ 认为我某个部分的设计过于繁琐,有更加简单或者更高逼格的封装方式
✅ 认为我部分代码过于老旧,可以提供新的API或最新语法
✅ 对于文章中部分内容不理解
✅ 解答我文章中一些疑问
✅ 认为某些交互,功能需要优化,发现BUG
✅ 想要添加新功能,对于整体的设计,外观有更好的建议
✅ 一起探讨技术加qq交流群:
906392632
最后感谢各位的耐心观看,既然都到这了,点个 👍赞再走吧!