ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Vuex Getter 完全指南:基于 Vuex 4 的 store 派生状态计算、两种访问方式与 mapGetters 映射实战

Vuex Getter 完全指南:基于 Vuex 4 的 store 派生状态计算、两种访问方式与 mapGetters 映射实战 前端【免费下载链接】vuex️ Centralized State Management for Vue.js.项目地址https://gitcode.com/gh_mirrors/vu/vuex点击查看免费下载Vuex 的 Getter 是定义在 store 内部的计算属性用于基于 state 派生过滤、统计、排序等计算结果并在多个组件间共享复用。本文以 docs/ja/guide/getters.md英文原版见 docs/guide/getters.md为主线结合 Vuex 4 源码src/store-util.js、src/helpers.js与仓库示例完整讲解 Getter 的定义、属性式访问、方法式访问、mapGetters映射以及其背后的缓存与响应式机制读完即可在真实项目中正确、高效地使用 Getter。为什么需要 Getter从组件内联计算到 store 级派生状态在组件中我们常常需要基于 store 状态做派生计算例如过滤待办列表并统计已完成数量。最直接的做法是在组件的computed中写computed: { doneTodosCount () { return this.$store.state.todos.filter(todo todo.done).length } }问题在于如果多个组件都需要这份派生逻辑要么把函数复制多份重复代码要么抽成共享 helper 再在多个地方 import依然不够内聚。两种方式都不理想。Vuex 的解决方案是在 store 中定义getter把它看作store 的 computed 属性逻辑只写一次任何组件都能通过store.getters访问同一份计算结果。定义 Getter接收 state 作为第一个参数在创建 store 时通过getters选项定义import { createStore } from vuex const store createStore({ state: { todos: [ { id: 1, text: ..., done: true }, { id: 2, text: ..., done: false } ] }, getters: { doneTodos (state) { return state.todos.filter(todo todo.done) } } })Getter 的第一个参数永远是当前模块的state。定义好之后store.getters.doneTodos即可得到[{ id: 1, text: ..., done: true }]。从源码看getter 的实际注册发生在模块安装阶段installModule遍历模块的 getters通过registerGetter将原始 getter 包装进store._wrappedGetters见 src/store-util.js 与 src/store-util.js。包装函数会为 getter 依次传入四个参数store._wrappedGetters[type] function wrappedGetter (store) { return rawGetter( local.state, // 本地当前模块state local.getters, // 本地 getters store.state, // 根 state store.getters // 根 getters ) }也就是说除了文档明确说明的前两个参数state、gettersgetter 实际还能接收第 3、4 个参数根模块的state与根模块的getters这在模块化 store 中非常有用。属性式访问Property-Style Access支持 getter 级联与响应式缓存在 getter 中调用其他 getterGetter 会接收其他 getter作为第二个参数从而支持级联派生getters: { // ... doneTodosCount (state, getters) { return getters.doneTodos.length } }访问store.getters.doneTodosCount得到1。第二个参数getters中可用的其他 getter在不同场景下含义不同根模块中即全部根 getter在模块内部见下文模块与命名空间则指本地 getters 代理对象由makeLocalGetters创建见 src/store-util.js。在组件中使用任何组件内部都可以直接通过this.$store.getters使用computed: { doneTodosCount () { return this.$store.getters.doneTodosCount } }属性式访问的缓存机制以属性形式访问的 getter 会作为 Vue 响应式系统的一部分被缓存只要依赖的 state 未变化重复访问返回同一结果不会重复执行过滤计算。这一机制的底层实现位于 src/store-util.js 的resetStoreStatescope.run(() { forEachValue(wrappedGetters, (fn, key) { computedObj[key] partial(fn, store) computedCache[key] computed(() computedObj[key]()) Object.defineProperty(store.getters, key, { get: () computedCache[key].value, enumerable: true }) }) })可见store.getters上的每个 getter 实际是一个computed(() ...)对象Vue 3 的computed通过Object.defineProperty的 getter 暴露其.value。这些 computed 被包裹在一个独立创建的effectScope中src/store-util.js目的是让 getter 的响应式依赖不会因组件卸载而被销毁——这是 Vuex 4 为适配 Vue 3 组合式 API 做的关键设计。注意事项Vue 3.0 下的已知缓存问题原文档特别给出警告在 Vue 3.0 中getter 的结果不像 computed 那样被缓存这是当时的一个已知问题需等待 Vue 3.2 修复对应上游 PR 讨论。也就是说以属性式访问的 getter 缓存行为取决于你所用的 Vue 版本在 Vue 3.2 及以后版本中可稳定依赖其缓存语义若仍在使用 Vue 3.0则应意识到 getter 可能被重复求值避免在 getter 中放入昂贵或带副作用的计算。方法式访问Method-Style Access通过返回函数向 getter 传参当需要根据参数查询 store 中的数据例如按 id 查找数组元素时可以让 getter 返回一个函数getters: { // ... getTodoById: (state) (id) { return state.todos.find(todo todo.id id) } }调用方式变为函数调用store.getters.getTodoById(2) // - { id: 2, text: ..., done: false }关键差异通过方法访问的 getter 每次调用都会重新执行结果不会被缓存。因此适合按需查询、参数化取值的场景如按 id 查找、按关键词过滤不适合把昂贵计算放在内部——每次调用都会重新计算无法利用响应式缓存它本身是纯函数不会自动追踪依赖也不会自动响应 state 变化需要配合组件内的computed或手动重新求值才能获得响应式。对比可见属性式访问 缓存 响应式方法式访问 参数化 每次重算二者按需取舍。mapGetters辅助函数把 getter 映射为本地 computedmapGetters是 Vuex 提供的内置辅助函数作用是把 store 的 getter映射为组件本地的 computed 属性避免在模板中反复书写$store.getters.xxx。数组形式同名映射import { mapGetters } from vuex export default { // ... computed: { // 使用对象展开运算符把 getter 混入 computed ...mapGetters([ doneTodosCount, anotherGetter // ... ]) } }数组中的每个字符串既作为 store 中的 getter 名也作为组件本地 computed 名。对象形式重命名映射如果希望映射为不同名称使用对象形式...mapGetters({ // 将 this.doneCount 映射到 this.$store.getters.doneTodosCount doneCount: doneTodosCount })mapGetters 的源码实现mapGetters定义于 src/helpers.js其核心逻辑是对每个映射项生成一个名为mappedGetter的组件方法方法内部返回this.$store.getters[val]export const mapGetters normalizeNamespace((namespace, getters) { const res {} if (__DEV__ !isValidMap(getters)) { console.error([vuex] mapGetters: mapper parameter must be either an Array or an Object) } normalizeMap(getters).forEach(({ key, val }) { // 命名空间已被 normalizeNamespace 归一化自动补上结尾的 / val namespace val res[key] function mappedGetter () { if (namespace !getModuleByNamespace(this.$store, mapGetters, namespace)) { return } if (__DEV__ !(val in this.$store.getters)) { console.error([vuex] unknown getter: ${val}) return } return this.$store.getters[val] } // 为 devtools 标记 vuex getter res[key].vuex true }) return res })值得注意的实现细节归一化命名空间normalizeNamespacesrc/helpers.js会在 namespace 字符串不以/结尾时自动补/所以mapGetters(foo, ...)与mapGetters(foo/, ...)等价开发态校验当映射参数既不是数组也不是对象、或目标 getter 不存在时会在开发环境打印console.error对应测试 test/unit/helpers.spec.js 验证了参数非法与getter 未定义的错误提示命名空间校验使用命名空间时若store._modulesNamespaceMap[namespace]中找不到对应模块会打印[vuex] module namespace not found in mapGetters(): ...并静默返回映射出的方法带有vuex true标记供 Vue Devtools 识别。命名空间下的 mapGetters当 store 采用带命名空间的模块时可以把命名空间作为mapGetters的第一个参数computed: { ...mapGetters(foo, { a: hasAny, b: negative }) }对应测试 test/unit/helpers.spec.js 验证了在namespaced: true的foo模块中mapGetters(foo, { a: hasAny })实际读取的是store.getters[foo/hasAny]并会随store.commit(foo/inc)响应式更新。多层嵌套命名空间同理可写作mapGetters(foo/bar, ...)见 test/unit/helpers.spec.js。模块与命名空间中的 Getterlocal state / local getters 的语义当 getter 定义在模块内时第一个参数是 state这句话需要更精确的表述传入的是该模块的 local state而不是根 state。同理第二个参数getters在该模块内部指向本地 getters 代理由makeLocalGetters在首次访问时构建并缓存到store._makeLocalGettersCache见 src/store-util.js代理只暴露属于该命名空间下的 getter。来看仓库中的真实示例 examples/classic/chat/store/getters.js它展示了 getter 的典型组合用法——级联、解构 state、引用其他 getterexport const threads state state.threads export const currentThread state { return state.currentThreadID ? state.threads[state.currentThreadID] : {} } export const currentMessages state { const thread currentThread(state) return thread.messages ? thread.messages.map(id state.messages[id]) : [] } export const unreadCount ({ threads }) { return Object.keys(threads).reduce((count, id) { return threads[id].lastMessage.isRead ? count : count 1 }, 0) } export const sortedMessages (state, getters) { const messages getters.currentMessages return messages.slice().sort((a, b) a.timestamp - b.timestamp) }其中currentMessages在 getter 内部直接调用另一个 getter 函数currentThread(state)——这是函数式拆分 getter 的常用写法而sortedMessages则通过第二个参数getters引用getters.currentMessages完成级联派生。组件侧则在 examples/classic/chat/components/MessageSection.vue 用mapGetters把currentThread、sortedMessages映射为本地 computedcomputed: mapGetters({ thread: currentThread, messages: sortedMessages })而在 examples/classic/shopping-cart/store/modules/products.js 中可以看到模块化 store 的完整形态namespaced: truestate/getters/actions/mutations拆分命名空间模块下的 getter 需以模块名/getter名形式访问。Composition API 下如何使用 GetterVuex 4 全面支持 Vue 3 组合式 API。在script setup或setup()中可以使用useStore()获取 store 实例配合 src/injectKey.js 中导出的注入 key再以与选项式 API 相同的两条路径访问 getterimport { useStore } from vuex import { computed } from vue const store useStore() // 属性式访问配合 computed 获得响应式 const doneTodosCount computed(() store.getters.doneTodosCount) // 方法式访问参数化查询 const todoById (id) store.getters.getTodoById(id)注意两点store.getters本身是响应式的底层是 computed 集合但解构取值如const { doneTodosCount } store.getters会丢失响应性务必通过computed(() store.getters.xxx)包裹若使用命名空间模块可借助createNamespacedHelperssrc/helpers.js生成预绑定命名空间的mapGetters等辅助函数减少重复书写命名空间前缀。更多组合式写法可参考仓库 docs/guide/composition-api.md 与 examples/composition 目录下的示例。实战要点速查场景推荐写法缓存行为过滤/统计/排序等纯派生计算属性式访问 computed响应式缓存Vue 3.2按 id、关键词等参数查询方法式访问getter 返回函数每次调用重新执行组件内省去$store.getters前缀mapGetters([...])/mapGetters({ 别名: 原名 })与属性式访问一致命名空间模块store.getters[foo/bar]或mapGetters(foo/bar, ...)与属性式访问一致Composition APIcomputed(() store.getters.x)与属性式访问一致最后给出几条实践中最重要的原则getter 必须是纯函数——只基于state/getters参数计算不要在 getter 内直接修改 state修改 state 只能通过 mutation参见 docs/guide/mutations.md优先属性式访问让 Vue 的 computed 缓存帮你避免重复计算只有需要参数化查询时才用方法式访问并注意其不缓存的开销getter 可以被其他 getter 组合善用(state, getters)第二个参数把复杂派生拆成小而可复用的 getter 链多组件共享的派生逻辑都应下沉到 store 的 getter 中这正是 Vuex集中式状态管理的核心价值之一。关于 getter 在模块化 store、热重载hot reloadgetter 会在 src/store-util.js 的resetStore流程中被重建与严格模式下的行为可继续阅读仓库的 docs/guide/modules.md 与 docs/guide/strict.md。赞分享前端【免费下载链接】vuex️ Centralized State Management for Vue.js.项目地址https://gitcode.com/gh_mirrors/vu/vuex点击查看免费下载相关推荐Vuex 4 State 状态管理完全指南单一状态树、$store 注入与 mapState 辅助函数实战Vuex 4 State 状态管理完全指南单一状态树、$store 注入与 mapState 辅助函数实战 本文以 Vuex 官方文档的 State 章节为骨前端30 分钟跑通 pgvector 的 Windows 编译从克隆到相似度查询的完整实操30 分钟跑通 pgvector 的 Windows 编译从克隆到相似度查询的完整实操 pgvector 是 Postgres 生态里最常用的向量相似度搜索扩前端Vuex 4 Composition API 实战指南useStore 与组合式状态管理完全解析Vuex 4 Composition API 实战指南useStore 与组合式状态管理完全解析 Vuex 4 是面向 Vue 3 的集中式状态管理方案为前端上一篇OpenCore Legacy Patcher终极指南五步让你的老Mac焕发新生下一篇ComfyUI-Workflows-ZHOAI创作终极指南与完整中文工作流集合创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表