定义(看下Copilot的回答)
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 被设计用于中大型单页应用程序(SPA),为不同组件间的共享状态提供了一种统一的方式。
核心概念
-
State(状态):
- Vuex 使用单一状态树 —— 一个对象包含了全部的应用层级状态。作为一个“唯一数据源(SSOT)”,它使得状态在应用中保持一致。
-
Getters(获取器):
- 类似于组件的计算属性。当你需要从 store 的 state 中派生出一些状态时,可以使用 getters。
-
Mutations(变更):
- 更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutations 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数。
-
Actions(动作):
- Actions 类似于 mutations,不同之处在于:
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作。
- Actions 类似于 mutations,不同之处在于:
-
Modules(模块):
- 由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就可能变得相当臃肿。为了解决这个问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter 甚至是嵌套子模块。
工作流程
- 组件内部通过
dispatch方法触发 actions。 - Actions 内部可以执行异步操作,然后通过
commit方法触发 mutations。 - Mutations 修改 state,Vue 组件会自动响应状态变化。
使用场景
- 多个视图依赖于同一状态。
- 来自不同视图的行为需要变更同一状态。
Vuex 提供了一种机制将组件的共享状态外提到一个全局单例中管理,使得你可以在应用中的任何位置访问或修改状态。
源码分析
- vue使用vuex
// 实例化vuex
const store = createStore({
// 两个模块
modules: {
cart,
products
},
strict: debug,
// 日志记录器的工厂函数 提供了灵活的日志记录能力,允许开发者根据需要配置日志的详细程度和格式
// 非常适合在开发过程中跟踪和调试状态管理相关的问题
plugins: debug ? [createLogger()] : []
})
export default store;
// vue.use安装插件,store对象含有install方法
vue.use(store)
// store类的install方法
install (app, injectKey) {
// 将store对象注入到所有的子组件中
app.provide(injectKey || storeKey, this)
app.config.globalProperties.$store = this
}
- vuex的实例化
export function createStore (options) {
return new Store(options)
}
export class Store {
constructor (options = {}) {
// 设置和管理vuex的模块集合
this._modules = new ModuleCollection(options)
// 初始化根模块
// 这也递归地注册所有子模块
// 收集this. _wrappegetters中的所有模块getter
installModule(this, state, [], this._modules.root)
// 初始化负责响应性的存储状态
// 同时注册_wrappegetters作为计算属性
resetStoreState(this, state)
// apply plugins
plugins.forEach(plugin => plugin(this))
}
}
- ModuleCollection是一个vuex store设置和管理所有的模块的类
export default class ModuleCollection {
// rawRootModule 实例化vuex时的options
constructor (rawRootModule) {
// register root module (Vuex.Store options)
this.register([], rawRootModule, false)
}
get (path) {
return path.reduce((module, key) => {
return module.getChild(key)
}, this.root)
}
getNamespace (path) {
let module = this.root
return path.reduce((namespace, key) => {
module = module.getChild(key)
return namespace + (module.namespaced ? key + '/' : '')
}, '')
}
update (rawRootModule) {
update([], this.root, rawRootModule)
}
register (path, rawModule, runtime = true) {
if (__DEV__) {
assertRawModule(path, rawModule)
}
const newModule = new Module(rawModule, runtime)
if (path.length === 0) {
this.root = newModule
} else {
const parent = this.get(path.slice(0, -1))
parent.addChild(path[path.length - 1], newModule)
}
// register nested modules
if (rawModule.modules) {
forEachValue(rawModule.modules, (rawChildModule, key) => {
this.register(path.concat(key), rawChildModule, runtime)
})
}
}
unregister (path) {
const parent = this.get(path.slice(0, -1))
const key = path[path.length - 1]
const child = parent.getChild(key)
if (!child) {
if (__DEV__) {
console.warn(
`[vuex] trying to unregister module '${key}', which is ` +
`not registered`
)
}
return
}
if (!child.runtime) {
return
}
parent.removeChild(key)
}
isRegistered (path) {
const parent = this.get(path.slice(0, -1))
const key = path[path.length - 1]
if (parent) {
return parent.hasChild(key)
}
return false
}
}