Vuex 4 快速入门:从零构建你的第一个集中式状态管理 Store
【免费下载链接】vuex🗃️ Centralized State Management for Vue.js.项目地址: https://gitcode.com/gh_mirrors/vu/vuex
Vuex 是 Vue.js 官方的集中式状态管理模式与库,而docs/guide/index.md这篇入门指南回答了一个核心问题:如何创建并安装你的第一个 Vuex Store。本文以该入门指南为主体,结合本仓库(Vuex 4)的源码实现、单元测试与经典 counter 示例,系统讲解 Store 的两大设计原则、最简 Store 的完整搭建流程、store.state/store.commit的访问方式,以及组件内this.$store的用法,最终带你掌握在 Vue 3 应用中落地 Vuex 状态管理的完整实战路径。
什么是 Store:一切状态的中心容器
在每一个 Vuex 应用的中央,都站立着一个store(存储容器)。通俗地说,store 就是一个"盒子",它装着你的应用状态(state)——即应用中所有需要被共享、驱动视图渲染的数据源。
Vuex 的 store 与普通的全局对象(global object)有本质区别,体现在两条核心设计原则上:
- Vuex 的 store 是响应式的(reactive)。当 Vue 组件从 store 中读取状态时,如果 store 的状态发生了变化,组件会响应式地、高效地自动更新,无需手动触发重渲染。
- 你不能直接修改 store 中的状态。改变状态的唯一途径,是显式地提交(commit)一个 mutation(变更)。这一约定保证了每一次状态变更都能留下可追踪的记录,并让诸如日志记录、状态快照、时间旅行调试(time travel debugging)等工具成为可能。
从源码层面看,这两条原则有非常具体的落点。Vuex 4 中 Store 的响应式由 Vue 3 的reactiveAPI 承载:在 src/store-util.js 的resetStoreState中,store 的内部状态被包装为reactive({ data: state }),并通过store.state的 getter 暴露出来(见 src/store.js);而"只能通过 mutation 修改状态"则由_withCommit标志位与严格模式守护,commit()内部会先将this._committing置为true,再依次执行所有 mutation handler(见 src/store.js 与 src/store.js)。
在继续阅读之前需要说明:本文代码示例采用 ES2015 语法(包括
import、const、箭头函数等),这也是 Vuex 官方文档余下所有章节的统一风格。
准备工作:安装 Vuex 4
在创建第一个 store 之前,需要先安装 Vuex。由于本仓库是 Vuex 4(面向 Vue 3 的版本),官方提供了多种安装方式,详见 安装指南:
通过包管理器安装(推荐):
# npm npm install vuex@next --save # yarn yarn add vuex@next --save通过 CDN 直接引入:
在 Vue 之后引入vuex的构建文件,它会自动完成安装:
<script src="/path/to/vue.js"></script> <script src="/path/to/vuex.js"></script>CDN 链接始终指向 NPM 上的最新 release;如需指定版本,可使用https://unpkg.com/vuex@4.0.0/dist/vuex.global.js这类带版本号的 URL。
使用开发版构建:
如果你希望使用最新的开发构建,则需要克隆 Vuex 源码仓库并自行构建:
git clone https://github.com/vuejs/vuex.git node_modules/vuex cd node_modules/vuex yarn yarn build安装完成后,即可通过import { createStore } from 'vuex'引入createStore工厂函数。在 src/index.js 中可以看到,Vuex 4 对外导出了createStore、Store、storeKey、useStore、mapState、mapGetters、mapActions、mapMutations、createNamespacedHelpers、createLogger等全部公共 API。
创建最简 Store:state 与 mutations
创建一个 Vuex store 非常简单直接:只需提供一个初始状态对象和若干mutations(变更)。下面是最小的完整示例:
import { createApp } from 'vue' import { createStore } from 'vuex' // 创建一个新的 store 实例。 const store = createStore({ state () { return { count: 0 } }, mutations: { increment (state) { state.count++ } } }) const app = createApp({ /* your root component */ }) // 将 store 实例作为插件安装 app.use(store)注意这里有两个关键细节:
state是一个返回对象的函数,而不是一个普通对象字面量。在 examples/classic/counter/store.js 中则直接使用了对象字面量写法(const state = { count: 0 }),两种写法在根 store 中都可用,但函数形式在多模块场景下能避免状态对象被多个模块共享引用。mutations中的每个 handler 接收state作为第一个参数,并在其中直接修改状态——例如state.count++。
createStore只是new Store(options)的语法糖(见 src/store.js)。Store 构造器会依次完成:初始化内部注册表(_actions、_mutations、_wrappedGetters、_modules等)、递归安装所有模块、初始化响应式状态、最后应用插件(见 src/store.js)。
读取状态与提交变更:store.state 与 store.commit
store 创建完成后,你就可以通过store.state访问状态对象,并通过store.commit触发状态变更:
store.commit('increment') console.log(store.state.count) // -> 1这段代码的执行链路在源码中非常清晰:store.commit('increment')会在_mutations表中查找'increment'对应的 handler 列表,并在_withCommit的保护下依次执行,随后通知所有订阅者(subscribers)(见 src/store.js)。commit还支持对象风格的调用,这在 test/unit/store.spec.js 中有专门用例验证:
store.commit({ type: TEST, amount: 2 }) // 等价于 store.commit(TEST, { amount: 2 })这一写法由 src/store-util.js 中的unifyObjectStyle统一解析,它同时服务于commit与dispatch,并且在开发环境下会对非字符串的 type 抛出断言错误(见 test/unit/store.spec.js 的对应测试)。
为什么必须 commit 而不是直接改store.state.count?
入门指南强调:我们选择提交 mutation 而非直接修改store.state.count,是因为我们想显式地追踪它。这个简单的约定让代码意图更加明确——阅读代码时,你能一眼看出状态是在哪里、因为什么而改变的。更重要的是,它为工具链打开了大门:可以记录每一次 mutation、拍摄状态快照、甚至实现时间旅行调试。
在仓库中,mutation 的"可追踪性"有两个具体实现:其一,commit()执行后会调用this._subscribers.slice().forEach(sub => sub(mutation, this.state)),将 mutation 对象与最新状态广播给订阅者(见 src/store.js),这正是 devtool 插件 与日志插件工作的基础;其二,在开发模式下,_withCommit标志位配合严格模式会在状态被非法修改时给出明确报错(见 src/store-util.js 的enableStrictMode)。
在 Vue 组件中使用 store:this.$store
在 Vue 组件中,你可以通过this.$store访问到 store 实例,然后像下面这样在组件方法中提交 mutation:
methods: { increment() { this.$store.commit('increment') console.log(this.$store.state.count) } }this.$store的注入发生在Store.install方法中:当执行app.use(store)时,Vuex 会通过app.provide(injectKey || storeKey, this)将 store 提供给整个组件树,同时将其挂载到app.config.globalProperties.$store上(见 src/store.js),这就是任意层级的组件都能拿到this.$store的原因。默认的storeKey就是字符串'store'(见 src/injectKey.js)。
用计算属性读取响应式状态
在组件中使用 store 状态的推荐方式是:在 computed 属性(计算属性)中返回该状态,因为 store 的状态本身就是响应式的;而触发变更则是在组件方法中提交 mutation:
computed: { count () { return this.$store.state.count } }这背后的响应式链路是:store 状态被reactive()包装,组件中的计算属性读取它时建立依赖,一旦 mutation 修改了状态,依赖它的计算属性会自动重新求值,进而驱动视图更新。
仓库中的 examples/classic/counter/Counter.vue 给出了一个完整的实战范本:模板中直接用{{ $store.state.count }}渲染状态,通过mapGetters与mapActions辅助函数把 getter 映射为计算属性、把 action 映射为方法;对应的 examples/classic/counter/app.js 展示了标准的挂载流程(createApp(Counter)→app.use(store)→app.mount('#app'))。完整的 counter store(含increment、decrement、incrementIfOdd、incrementAsync等动作)见 examples/classic/counter/store.js。
背后的原理:响应式与可追踪性的源码印证
入门指南反复强调的两个特性,可以在源码中找到完整印证:
响应式(Reactive)。resetStoreState使用reactive({ data: state })包装根状态,并将 getters 包装为基于computed的惰性缓存属性——为避免 getter 在组件卸载时被销毁,还特意用effectScope承载这些 computed(见 src/store-util.js)。这就是"组件读取 store 状态后能响应式高效更新"的实现基础。
可追踪(Trackable)。除了commit广播订阅者之外,store 还提供了subscribe与subscribeAction方法(见 src/store.js),让插件可以监听每一次 mutation 与 action 的生命周期;replaceState(src/store.js)则在_withCommit保护下显式替换整个状态树——这正是状态快照与时间旅行调试能力得以存在的前提。
小结与下一步
入门指南用一句话总结了 Vuex 的使用心智模型:在组件中使用 store 状态,就是在计算属性中返回该状态;触发变更,就是在组件方法中提交 mutation。围绕 store 的两个核心特性——响应式读取、显式提交变更——你已经可以写出第一个可用的 Vuex 应用。
入门之后,官方指南按以下顺序深入讲解每个核心概念,你可以顺着阅读:
- State(状态)——在组件与
mapState中访问状态的完整方式 - Getters(派生状态)——像计算属性一样对 store 状态做派生与复用
- Mutations(变更)——提交载荷(payload)、对象风格提交与响应式规则
- Actions(动作)——异步操作与组合 dispatch
- Modules(模块)——把大型 store 拆分为命名空间模块
- Plugins(插件)——订阅 mutation/action 实现日志、持久化等能力
- Strict Mode(严格模式)——强制所有状态变更必须发生在 mutation 中
- 组合式 API 用法——在
<script setup>中通过useStore消费 store - TypeScript 支持 与 从 Vuex 3 迁移到 4
完整的 API 说明可查阅 API 参考文档。此外,本仓库的 examples/classic 与 examples/composition 目录下还提供了 counter、chat、shopping-cart、todomvc 等可直接运行参考的完整示例应用,可以作为你动手实践的最佳样板。
【免费下载链接】vuex🗃️ Centralized State Management for Vue.js.项目地址: https://gitcode.com/gh_mirrors/vu/vuex
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考