ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Vuex 状态管理模式与集中式 Store 原理详解:从单向数据流到可预测状态变更

Vuex 状态管理模式与集中式 Store 原理详解:从单向数据流到可预测状态变更 前端【免费下载链接】vuex️ Centralized State Management for Vue.js.项目地址https://gitcode.com/gh_mirrors/vu/vuex点击查看免费下载Vuex 是 Vue.js 官方配套的状态管理模式 库它为应用中的所有组件提供一个集中化的 store并借助强制规则保证状态只能以可预测的方式被变更。本文以仓库 docs/ptbr/index.md官方「O que é Vuex?」导读为核心骨架结合仓库中的 Store 核心实现 与 counter 官方示例 源码系统讲解 Vuex 诞生的动机、核心概念、工作原理、适用场景以及从 Vuex 3 迁移到 Vuex 4Vue 3 版的关键差异。注本文面向 Vuex 4配合 Vue 3 使用。如果你在寻找配合 Vue 2 使用的 Vuex 3 文档可参考仓库 docs 目录中docs/guide/migrating-to-4-0-from-3-x.md对应的迁移指南。一、Vuex 是什么状态管理模式 库Vuex 的定义包含两个层次一种状态管理模式pattern规定了一套清晰的职责划分——把状态从组件中抽离出来集中管理并要求状态只能通过固定规则变更一个库library把上述模式落地为可直接使用的实现并且是专门为 Vue.js 量身定制的能够充分利用 Vue 的细粒度响应式系统实现高效更新这一特点与 Flux、Redux、Elm Architecture 等纯模式/通用实现不同。在仓库中Vuex 对外暴露为一个插件对象入口位于 src/index.js它导出Store、createStore、useStore、storeKey、mapState、mapMutations、mapGetters、mapActions、createNamespacedHelpers、createLogger等全部公共 API。其中createStore只是new Store(options)的一层语法糖export function createStore (options) { return new Store(options) }在 Vuex 4 中推荐以createStore创建 store然后通过app.use(store)安装为 Vue 插件——Store.install内部通过app.provide注入 store并挂载到app.config.globalProperties.$store见 src/store.js。二、什么是状态管理模式从一个计数器应用说起文档用一个最简 Vue 计数器应用说明状态管理的三要素const Counter { // state状态驱动应用的数据源 data () { return { count: 0 } }, // view视图状态的声明式映射 template: div{{ count }}/div , // actions操作响应视图上的用户输入、可能改变状态的方式 methods: { increment () { this.count } } } createApp(Counter).mount(#app)这个自包含应用由三部分构成构成了经典的单向数据流one-way data flow状态state——驱动应用的数据源视图view——将状态以声明方式映射出来的界面操作actions——响应视图上的用户输入、导致状态发生变化的途径。数据沿状态 → 视图 → 操作 → 状态的单向环路流动这正是 docs/guide/index.md 中反复强调的可预测性基础。单向数据流在组件共享状态时为何会失效当应用只有一两个组件时上述模式足够简洁但一旦出现多个组件共享同一份状态问题立刻浮现问题一多个视图可能依赖同一份状态。此时靠props逐层传递对深层嵌套的组件来说极为繁琐而且对兄弟组件之间的状态共享完全无能为力问题二不同视图的行为需要变更同一份状态。开发者往往被迫去拿父子实例的引用直接操作或者通过事件去变更、同步多份状态拷贝。这两种做法都非常脆弱很快会把代码推向无法维护的境地。文档明确指出传参props与事件events都不是为跨组件共享状态设计的方案。解决方案把共享状态抽离成全局单例既然共享状态放在组件内部必然导致传递灾难那就把它**从组件中抽取出来放进一个全局单例global singleton**统一管理。这样整个组件树就变成了一棵巨大的视图无论组件在树的哪个位置都可以访问状态或触发动作同时通过定义并隔离状态管理中的各个概念、用规则维持视图与状态的独立性代码会获得更强的结构与可维护性。这正是 Vuex 背后的基本思想。它的灵感来源于Flux、Redux和Elm Architecture但与其他模式不同Vuex 同时是专门针对 Vue.js 适配的库实现借助 Vue 的细粒度响应式系统实现高效的状态更新。三、Store 的两条核心规则响应式 只能通过 mutation 变更在仓库文档 docs/guide/index.md 中Vuex 的 store 之所以不同于普通的全局对象有两条根本规则Vuex 的 store 是响应式的当 Vue 组件从 store 读取状态时若 store 的状态发生变化组件会响应式且高效地自动更新不能直接修改 store 的状态改变状态的唯一方式是显式提交 mutationcommit。这保证了每一次状态变更都有迹可循也让记录每个 mutation、拍摄状态快照、甚至时间旅行调试这类工具成为可能。这两条规则在源码中有直接体现。在 src/store.js 中commit的实现会通过unifyObjectStyle归一化参数兼容commit(type, payload)与commit({ type, payload })两种对象风格调用对应测试用例见 test/unit/store.spec.js查找this._mutations[type]未注册的 mutation 类型在开发模式下会报unknown mutation type警告在_withCommit包裹下依次执行该类型的所有 handler随后通知所有订阅者_subscribers。而状态本身被存放在reactive({ data: state })之中见 src/store-util.jsstore.state的 getter 直接返回this._state.datasrc/store.js这正是store 状态响应式的底层来源。若在严格模式下enableStrictMode会用watch(..., { deep: true, flush: sync })深度监听状态任何发生在 mutation handler 之外的状态修改都会在开发环境断言失败见 src/store-util.js。四、Store 与普通全局对象的本质区别结合源码看响应式与可追踪性4.1 状态容器与响应式底层从 src/store.js 的构造函数可以看到Vuex 4 的Store在初始化时会读取options中的plugins、strict、devtools等配置用ModuleCollection递归收集根模块与所有子模块对应 src/module/module-collection.js通过installModule递归注册所有 mutation、action、getter 并建立命名空间映射通过resetStoreState把根状态放入reactive()并基于computed包装所有 getter——这样 getter 具备惰性缓存能力且通过effectScope隔离避免组件卸载时误销毁src/store-util.js依次调用plugins。4.2 mutation 与 action 的分工Vuex 刻意区分了两种改变状态的途径这是整个模式可预测性的关键概念职责是否允许异步底层实现mutation唯一允许实际改写 state 的入口handler 以整个 state 树为第一参数必须同步commit()在_withCommit中同步执行 handlersrc/store.jsaction执行副作用、组织异步流程通过commit间接提交 mutation允许异步dispatch()将结果包装成 Promise 返回src/store.js值得注意的源码细节是action 的返回值会被统一包装为 Promise见 src/store-util.js 中registerAction对isPromise(res)的判断因此dispatch天然支持await而 mutation 必须保持同步是为了让 DevTools 能精确记录每一次状态变更、进行快照对比与时间旅行调试。在仓库的 counter 官方示例 中这一分工体现得非常直观import { createStore } from vuex // 根状态对象每个 Vuex 实例就是一棵单一状态树 const state { count: 0 } // mutations 是真正改写状态的运算 // 每个 mutation handler 以整个状态树为第一个参数 // 后续参数是附加的 payload。mutation 必须同步 // 这样才能被插件记录用于调试。 const mutations { increment (state) { state.count }, decrement (state) { state.count-- } } // actions 是引发副作用、可能包含异步操作的函数 const actions { increment: ({ commit }) commit(increment), decrement: ({ commit }) commit(decrement), incrementIfOdd ({ commit, state }) { if ((state.count 1) % 2 0) { commit(increment) } }, incrementAsync ({ commit }) { return new Promise((resolve, reject) { setTimeout(() { commit(increment) resolve() }, 1000) }) } } // getters 是纯函数 const getters { evenOrOdd: state state.count % 2 0 ? even : odd } // 一个 Vuex 实例由 state、mutations、actions、getters 组合而成 export default createStore({ state, getters, actions, mutations })对应的组件通过mapGetters与mapActions消费 storeexamples/classic/counter/Counter.vuetemplate div idapp Clicked: {{ $store.state.count }} times, count is {{ evenOrOdd }}. button clickincrement/button button clickdecrement-/button button clickincrementIfOddIncrement if odd/button button clickincrementAsyncIncrement async/button /div /template script import { mapGetters, mapActions } from vuex export default { computed: mapGetters([ evenOrOdd ]), methods: mapActions([ increment, decrement, incrementIfOdd, incrementAsync ]) } /script4.3 组件如何拿到 storeVuex 4 与 Vue 3 的集成方式如下examples/classic/counter/app.jsimport { createApp } from vue import Counter from ./Counter.vue import store from ./store const app createApp(Counter) app.use(store) app.mount(#app)app.use(store)会触发Store.install一方面app.provide(storeKey, this)让所有组件都能通过注入拿到 store另一方面把$store挂到全局属性上于是组件内既可以用this.$store访问也可以在组合式 API 中用useStore()实现见 src/injectKey.js它本质是对inject(storeKey)的封装。4.4 可追踪性与调试工具每个 mutation 都留下记录并非空话——subscribe与subscribeAction把变更事件暴露给外部而 src/plugins/devtool.js 正是基于它们对接 Vue DevTools为每次 mutation 增加vuex:mutations时间线事件、为每次 action 记录 start/end 及耗时并提供一个可树形浏览各模块 state/getters 的 Vuex inspector。这就是官方文档所说的记录每次 mutation、拍摄状态快照、时间旅行调试能力的具体实现。五、什么时候应该使用 Vuex文档对这一问题的回答非常务实Vuex 帮助我们解决共享状态管理但代价是引入更多概念和样板代码这是短期生产力与长期生产力之间的一次权衡如果你的应用足够简单不使用 Vuex 通常也没问题。文档明确建议简单的应用用一个朴素的 store 模式把共享状态抽到一个模块里导出即可可能就足够了直接上 Vuex 反而会显得啰嗦、劝退如果你正在构建中大型 SPA你大概率已经遇到如何在 Vue 组件之外更好地管理状态的处境此时 Vuex 会是自然而然的下一个台阶——集中式状态、强制变更规则、DevTools 可追踪会让长期维护成本显著下降。Redux 作者 Dan Abramov 对此有一个广为流传的比喻文档也引用了它Flux 库就像眼镜你自会知道什么时候需要它。六、Vuex 4 与 Vuex 3 的差异要点面向 Vue 3关联文档开头的 NOTE 明确说明本文档对应 Vuex 4配合 Vue 3 使用需要 Vuex 3配合 Vue 2文档的读者应另寻 Vuex 3 文档。两者最核心的差异在于创建方式Vuex 4 推荐createStore({ ... })createStore是new Store()的语法糖见 src/store.js并支持new Vuex.Store()的旧写法安装方式Vuex 4 通过app.use(store)以 Vue 3 插件机制安装而不是 Vue 2 的new Vue({ store })组合式 APIVuex 4 提供useStore()src/injectKey.js配合script setup或组合式 API 使用源码级结构调整Vuex 4 的Store构造与 Vue 3 的reactive、computed、effectScope、watch深度绑定见 src/store.js 与 src/store-util.js因此它只能在 Vue 3 环境下运行。仓库还提供了一份完整的 从 3.x 迁移到 4.0 的指南其中列出了所有破坏性变更与迁移步骤。七、动手实践从零跑通一个 Vuex 应用仓库的 examples/classic/counter 与 examples/composition/counter 是可直接运行的最小示例前者展示this.$store/mapGetters等选项式写法后者展示组合式 API 写法。你可以在本仓库中对照阅读store 定义state、mutations、actions、getters 的完整组合应用入口app.use(store)安装流程组件消费$store.state、mapGetters、mapActions的使用配套测试committing mutations、object style commit、dispatching actions等用例覆盖了核心 API 的行为契约。八、继续深入从导读走向完整指南本文是 Vuex 的入门导读。基于同一个 store 核心src/store.js仓库 docs 目录还提供了完整的进阶指南建议按以下顺序继续阅读均为仓库内路径快速上手 Getting Startedstore 的最小创建与安装示例以及store.state/store.commit的首次使用State单一状态树与mapStateGetters类似计算属性的派生状态Mutations同步变更与提交风格Actions异步流程与组合Modules命名空间与模块化组织Strict Mode严格模式与store.replaceState插件与热重载、热重载扩展与开发体验组合式 API 用法useStore的完整实践。结语回到文档最初的定义Vuex 是状态管理模式 库。模式层面它把状态、视图、操作三者用单向数据流组织起来并在多组件共享状态时把状态抽离为全局单例库层面它通过响应式状态容器、强制commit变更、同步 mutation / 异步 action 的分工、可订阅的变更记录把可预测的状态变更落到了实处。是否引入它取决于你的应用规模与团队对长期可维护性的诉求——正如那句老话Flux 库就像眼镜你自会知道什么时候需要它。赞分享前端【免费下载链接】vuex️ Centralized State Management for Vue.js.项目地址https://gitcode.com/gh_mirrors/vu/vuex点击查看免费下载相关推荐Vuex 状态管理指南从单向数据流到集中式 Store 架构Vuex 状态管理指南从单向数据流到集中式 Store 架构 Vuex 是 Vue.js 应用的状态管理模式与库它把整个应用共享的状态集中到一个全局 Sto前端PrimeReact状态管理模式原子化状态与不可变数据PrimeReact状态管理模式原子化状态与不可变数据 在React应用开发中状态管理是决定应用性能和可维护性的核心环节。PrimeReact作为完整的Re前端UI组件PDF补丁丁完整教程如何免费修复PDF书签、合并拆分与去除限制PDF补丁丁完整教程如何免费修复PDF书签、合并拆分与去除限制 PDF书签改名后点击报错、散落各处的扫描图片想合成一本、文档一打开就弹网页、想无损取出文档里的桌面应用文档上一篇DEAP进化算法深度解析收敛性理论与最优解证明指南下一篇终极Archery前端架构升级指南5步实现从jQuery到Vue.js的平滑过渡创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进