news 2026/6/15 8:29:09

在计算属性中获取 Vuex 状态是标准做法(附:Vue 3 计算属性详解及和 watch 对比)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
在计算属性中获取 Vuex 状态是标准做法(附:Vue 3 计算属性详解及和 watch 对比)

Vuex状态存储在计算属性中获取是最佳实践。


因为:

1)计算属性具有响应式特性,确保状态变化时组件自动更新;

2)计算结果会被缓存,优化性能;

3)避免在data中直接存储状态导致响应丢失。


推荐使用mapState/mapGetters辅助函数简化代码,同时应通过mutations修改状态。


Vue3中可使用Composition API的computed获取状态。


这种方式完美结合了Vue的响应式系统和计算属性优势。


计算属性是基于响应式依赖进行缓存的派生值,只有当依赖的响应式数据发生变化时才会重新计算。


watch 监听数据变化执行副作用。


在计算属性中获取 Vuex 状态

核心原理

Vuex的状态存储确实是响应式的,这意味着:

  1. 状态变化自动更新视图:当store中的状态发生变化时,依赖这些状态的组件会自动重新渲染

  2. 计算属性是最佳实践:在计算属性中返回Vuex状态,可以确保状态变化时组件正确响应

基本用法示例

// Vuex store const store = new Vuex.Store({ state: { count: 0, user: { name: 'John' } } }) // 组件中 export default { computed: { // 方式1:直接返回状态 count() { return this.$store.state.count }, // 方式2:使用mapState辅助函数(更简洁) ...mapState({ userName: state => state.user.name, // 或简写 count: 'count' }) } }

为什么使用计算属性?

  1. 响应式保证

    // ❌ 错误:在data中存储,不会响应变化 data() { return { localCount: this.$store.state.count // 只会获取初始值 } } // ✅ 正确:使用计算属性 computed: { count() { return this.$store.state.count // 始终保持同步 } }
  2. 性能优化

    • 计算属性会缓存结果,只有依赖的状态变化时才重新计算

    • 避免不必要的重新渲染

其他获取状态的方式

// 在组件方法中获取当前状态 methods: { doSomething() { console.log(this.$store.state.count) } } // 在生命周期钩子中 created() { console.log(this.$store.state.count) } // 使用getters(派生状态) computed: { doubleCount() { return this.$store.getters.doubleCount }, ...mapGetters(['userProfile', 'totalItems']) }

最佳实践建议

export default { computed: { // 使用辅助函数让代码更清晰 ...mapState({ // 重命名 currentUser: state => state.auth.user, // 简写形式 isLoading: 'loadingStatus' }), // 使用getters处理复杂逻辑 ...mapGetters([ 'filteredItems', 'hasPermission' ]), // 本地计算属性与store状态结合 formattedCount() { return `Count: ${this.count}` } } }

注意事项

  1. 避免直接修改state:始终通过commitmutations来修改状态

  2. 大型应用使用模块化:使用命名空间模块组织状态

  3. 考虑使用Vuex的Composition API(Vue 3):

    import { computed } from 'vue' import { useStore } from 'vuex' export default { setup() { const store = useStore() const count = computed(() => store.state.count) return { count } } }

总结:在计算属性中获取Vuex状态确实是标准做法,因为它完美利用了Vue的响应式系统和计算属性的缓存特性,确保状态变化时UI自动更新。


Vue 3 计算属性详解


计算属性是Vue中最核心和强大的特性之一,在Vue 3中得到了进一步优化和增强。


一、基本概念

1.1 什么是计算属性?

计算属性是基于响应式依赖进行缓存的派生值,只有当依赖的响应式数据发生变化时才会重新计算。

import { ref, computed } from 'vue' export default { setup() { const price = ref(100) const quantity = ref(2) // 计算属性 const total = computed(() => { return price.value * quantity.value }) return { price, quantity, total } } }

1.2 计算属性 vs 方法

const app = { setup() { const count = ref(0) // ❌ 方法:每次调用都会执行 const getDoubleMethod = () => { console.log('方法执行了') return count.value * 2 } // ✅ 计算属性:缓存结果,依赖不变不重新计算 const doubleComputed = computed(() => { console.log('计算属性执行了') return count.value * 2 }) return { count, getDoubleMethod, doubleComputed } } }

二、Vue 3 计算属性的两种形式

2.1 Options API 中的计算属性

export default { data() { return { firstName: '张', lastName: '三', items: [ { id: 1, name: '苹果', price: 10 }, { id: 2, name: '香蕉', price: 5 } ] } }, computed: { // 基本用法 fullName() { return this.firstName + this.lastName }, // 带setter的计算属性 reversedName: { get() { return this.lastName + this.firstName }, set(newValue) { const [last, first] = newValue.split('') this.lastName = last this.firstName = first } }, // 依赖数组的计算属性 totalPrice() { return this.items.reduce((sum, item) => sum + item.price, 0) } } }

2.2 Composition API 中的计算属性

import { ref, reactive, computed } from 'vue' export default { setup() { // 基本响应式数据 const firstName = ref('张') const lastName = ref('三') const items = reactive([ { id: 1, name: '苹果', price: 10 }, { id: 2, name: '香蕉', price: 5 } ]) // 只读计算属性 const fullName = computed(() => { return `${firstName.value}${lastName.value}` }) // 可写计算属性 const reversedName = computed({ get() { return `${lastName.value}${firstName.value}` }, set(newValue) { const [last, first] = newValue.split('') lastName.value = last firstName.value = first } }) // 依赖响应式对象的计算属性 const totalPrice = computed(() => { return items.reduce((sum, item) => sum + item.price, 0) }) // 计算属性依赖另一个计算属性 const formattedTotal = computed(() => { return `总计: ¥${totalPrice.value}` }) return { firstName, lastName, items, fullName, reversedName, totalPrice, formattedTotal } } }

三、高级用法

3.1 计算属性依赖关系追踪

import { ref, computed, watchEffect } from 'vue' export default { setup() { const a = ref(1) const b = ref(2) const c = ref(3) // 计算属性会自动追踪依赖 const sumAB = computed(() => { console.log('计算 sumAB') return a.value + b.value }) const finalResult = computed(() => { console.log('计算 finalResult') return sumAB.value + c.value }) // 只有a或b变化时,sumAB才会重新计算 // 只有sumAB或c变化时,finalResult才会重新计算 return { a, b, c, sumAB, finalResult } } }

3.2 计算属性与异步操作

import { ref, computed } from 'vue' export default { async setup() { const userId = ref(1) const users = ref([]) // 计算属性不能直接包含异步操作,但可以返回Promise const userInfo = computed(() => { // 返回一个基于userId的计算结果 const user = users.value.find(u => u.id === userId.value) return user ? `${user.name} (${user.email})` : '未知用户' }) // 如果需要异步计算,使用watch或单独的函数 const fetchUserData = async () => { const response = await fetch(`/api/users/${userId.value}`) users.value = await response.json() } return { userId, userInfo, fetchUserData } } }

3.3 性能优化:记忆化复杂计算

import { ref, computed } from 'vue' export default { setup() { const largeArray = ref([/* 大量数据 */]) const filterText = ref('') // 昂贵的计算 - 使用计算属性进行缓存 const filteredItems = computed(() => { console.time('filter计算') const result = largeArray.value.filter(item => item.name.includes(filterText.value) ) console.timeEnd('filter计算') return result }) // 进一步派生计算 const sortedItems = computed(() => { return [...filteredItems.value].sort((a, b) => a.price - b.price ) }) return { filterText, sortedItems } } }

四、实用技巧

4.1 条件计算属性

const enabled = ref(true) const data = ref([]) // 只有enabled为true时才计算 const processedData = computed(() => { if (!enabled.value) return [] return data.value.map(item => ({ ...item, processed: true })) })

4.2 计算属性与类型安全(TypeScript)

import { ref, computed } from 'vue' interface User { id: number name: string age: number } export default { setup() { const users = ref<User[]>([]) const minAge = ref(18) // TypeScript会自动推断类型 const filteredUsers = computed(() => { return users.value.filter(user => user.age >= minAge.value) }) // 明确指定类型 const averageAge = computed<number>(() => { if (filteredUsers.value.length === 0) return 0 const total = filteredUsers.value.reduce((sum, user) => sum + user.age, 0) return total / filteredUsers.value.length }) return { filteredUsers, averageAge } } }

4.3 调试计算属性

import { ref, computed, onRenderTracked, onRenderTriggered } from 'vue' export default { setup() { const a = ref(1) const b = ref(2) const sum = computed(() => { debugger // 可以在这里调试 return a.value + b.value }) // 调试依赖追踪 onRenderTracked((event) => { console.log('追踪依赖:', event) }) onRenderTriggered((event) => { console.log('触发更新:', event) }) return { a, b, sum } } }

五、注意事项和最佳实践

5.1 避免副作用

// ❌ 错误:在计算属性中产生副作用 const badComputed = computed(() => { console.log('副作用') // 不应该! updateDatabase() // 绝对不行! return someValue }) // ✅ 正确:计算属性应该是纯函数 const goodComputed = computed(() => { return a.value + b.value })

5.2 避免修改依赖

// ❌ 错误:在计算属性中修改依赖 const badComputed = computed(() => { a.value = a.value + 1 // 修改依赖,导致无限循环 return a.value * 2 })

5.3 计算属性的性能考虑

// 如果计算非常昂贵,考虑使用缓存或Web Worker const expensiveComputation = computed(() => { // 对于超大数据集,可能需要其他优化 return heavyDataProcessing(largeDataset.value) }) // 或者使用防抖计算 import { debounce } from 'lodash-es' const debouncedComputed = computed(() => { return debounce(() => { return processData(input.value) }, 300) })

六、与Vue 2的差异

特性Vue 2Vue 3
Composition API不支持原生支持
TypeScript支持有限完整的类型推断
响应式系统Object.definePropertyProxy
性能全部重新计算依赖更智能的依赖追踪
调试有限更好的开发工具支持

总结

Vue 3的计算属性在保持原有简洁性的基础上:

  1. 更好的性能:更智能的依赖追踪和缓存

  2. 更好的TypeScript支持:完整的类型推断

  3. 更灵活的组合:Composition API提供更好的代码组织

  4. 更好的调试体验:开发工具增强


计算属性的核心原则始终是:声明式、响应式、缓存和纯净。正确使用计算属性可以显著提升Vue应用的性能和可维护性。


Vue 3 计算属性 vs watch

特性维度计算属性 (computed)监听器 (watch/watchEffect)
主要目的派生新的响应式数据监听数据变化执行副作用
返回值必须有返回值不需要返回值
语法形式computed(() => expression)
computed({ get, set })
watch(source, callback, options)
watchEffect(callback)
执行时机立即执行并缓存结果默认惰性执行(watch)
立即执行(watchEffect)
缓存机制✅ 自动缓存依赖结果❌ 不缓存,每次触发都执行
依赖收集自动追踪响应式依赖watch: 需要指定依赖源
watchEffect: 自动收集
典型用例模板中的派生数据
过滤/排序列表
格式化显示数据
数据变化时发送请求
执行DOM操作
执行异步操作
性能优化依赖不变时不重新计算可配置deep、immediate、flush等选项
组合API示例const double = computed(() => count.value * 2)watch(count, (newVal) => { console.log(newVal) })
Options API示例computed: { fullName() { return this.first + this.last } }watch: { count(newVal) { console.log(newVal) } }
TypeScript支持完整的类型推断完整的类型推断
使用场景将已有数据转换为新格式
基于多个数据计算单一值
数据变化时需要执行操作
响应数据变化执行异步任务
副作用处理❌ 禁止副作用(纯函数)✅ 专门处理副作用
代码组织声明式,关注"是什么"命令式,关注"做什么"
调试友好度易于调试和测试需要更多调试考虑

详细对比示例

场景:用户数据展示

import { ref, computed, watch, watchEffect } from 'vue' export default { setup() { const user = ref({ name: 'Alice', age: 25 }) const discountRate = ref(0.1) const apiCallCount = ref(0) // ✅ 计算属性:派生数据 const userInfo = computed(() => { return `${user.value.name}, ${user.value.age}岁` }) const discountedAge = computed(() => { return user.value.age * (1 - discountRate.value) }) // ✅ watch:监听变化执行副作用 watch( () => user.value.age, (newAge, oldAge) => { console.log(`年龄从${oldAge}变为${newAge}`) apiCallCount.value++ // 可以执行API调用等副作用 }, { immediate: true } ) // ✅ watchEffect:自动收集依赖 watchEffect(() => { // 自动追踪user.value.name和discountRate.value if (user.value.name === 'Admin' && discountRate.value > 0.5) { console.warn('管理员折扣过高!') } }) return { user, discountRate, userInfo, // 计算属性 - 用于模板显示 discountedAge, // 计算属性 - 用于模板显示 apiCallCount // watch更新的数据 } } }

选择指南决策表

需求场景选择理由
基于现有数据计算显示值计算属性声明式、缓存、模板友好
数据变化时调用APIwatch专门处理副作用
实时验证表单字段watchEffect自动收集多个依赖
格式化日期/货币计算属性纯函数、可缓存
路由参数变化重新获取数据watch需要精确控制监听源
防抖搜索输入watch + 防抖函数需要控制执行时机
计算对象数组的总价计算属性基于依赖计算、高效
保存数据到localStoragewatch数据变化时的副作用
监听多个数据源组合watch([a, b], callback)多个精确依赖
权限检查计算属性返回布尔值用于v-if

性能对比示例

// 性能敏感场景选择 export default { setup() { const largeList = ref([/* 10000个项目 */]) const filterText = ref('') // ✅ 计算属性:高效,缓存过滤结果 const filteredList = computed(() => { // 只有在largeList或filterText变化时才重新计算 return largeList.value.filter(item => item.name.includes(filterText.value) ) }) // ❌ 不当使用watch:每次都要重新计算和赋值 const filteredList2 = ref([]) watch( [() => largeList.value, filterText], () => { // 即使依赖未变也会执行 filteredList2.value = largeList.value.filter(item => item.name.includes(filterText.value) ) }, { deep: true } // 深度监听更耗性能 ) return { filterText, filteredList, // ✅ 推荐:计算属性 filteredList2 // ❌ 不推荐:watch } } }

组合使用的最佳实践

export default { setup() { const userId = ref(1) const userData = ref(null) const loading = ref(false) // ✅ 计算属性:派生数据 const isAdult = computed(() => { return userData.value?.age >= 18 }) const userName = computed(() => { return userData.value?.name || '未知用户' }) // ✅ watch:监听变化执行异步操作 watch( userId, async (newId) => { loading.value = true try { const response = await fetch(`/api/users/${newId}`) userData.value = await response.json() } catch (error) { console.error('获取用户失败:', error) } finally { loading.value = false } }, { immediate: true } // 立即执行一次 ) // ✅ watchEffect:自动依赖收集 watchEffect(() => { // 当userData变化时更新document.title if (userData.value?.name) { document.title = `用户: ${userData.value.name}` } }) return { userId, userData, loading, isAdult, // 计算属性 - 用于条件渲染 userName // 计算属性 - 用于显示 } } }

关键总结

  1. 计算属性是"声明式"的:关心结果是什么

    • 适合:数据转换、格式化、过滤、计算值

    • 特点:缓存、纯函数、模板友好

  2. watch/watchEffect是"命令式"的:关心当数据变化时做什么

    • 适合:副作用操作、API调用、DOM操作、日志记录

    • 特点:灵活控制、副作用处理、异步支持

  3. 经验法则

    • 如果需要显示派生数据→ 用计算属性

    • 如果需要响应数据变化执行操作→ 用watch

    • 如果需要自动追踪多个依赖→ 用watchEffect

    • 如果计算昂贵且依赖变化不频繁→ 优先计算属性

    • 如果需要精确控制执行时机→ 用watch配置选项

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/15 16:32:25

系统思考:以客户为中心

周一的学习实验室&#xff0c;有小伙伴提到“控场力”。我当下的判断是&#xff1a;控场本身并不是能力&#xff0c;而是系统良性运转后的外显结果。 顺着这个判断继续拆&#xff0c;我们发现一个关键变量反复出现——是否真正以学员为中心、以客户为中心。 这让我想到企业里那…

作者头像 李华
网站建设 2026/6/15 11:39:56

计算机Java毕设实战-基于小程序的上班企业考勤签到签退下班打卡系统设计与实现【完整源码+LW+部署说明+演示视频,全bao一条龙等】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/6/15 14:11:00

书单推荐之豆包高效学习:AI时代的教育破局指南

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 &#x1f38f;&#xff1a;你只管努力&#xff0c;剩下的交给时间 &#x1f3e0; &#xff1a;小破站 书单推荐之豆包高效学习&#xff1a;AI时代的教育破局指南 为什么这本书值得每个家庭拥有&#x…

作者头像 李华
网站建设 2026/6/15 16:00:48

《以大制胜》精读笔记:与《影响力》《乌合之众》的非理性说服闭环

《影响力》精读笔记 《乌合之众》精读笔记 斯科特亚当斯在《以大制胜》中&#xff0c;以自身对说服术的终身研究为基础&#xff0c;结合2016年美国大选等真实案例&#xff0c;拆解了“武器级”说服的底层逻辑与实操方法。这本书并非单纯的技巧合集&#xff0c;更像是对人类非理…

作者头像 李华
网站建设 2026/6/15 9:34:06

Java计算机毕设之基于springboot的办公用品库存采购下发管理系统小程序的设计与实现(完整前后端代码+说明文档+LW,调试定制等)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/6/15 14:31:32

System Operations Management 1

1. Maintenance 维护 Explanation: The process of keeping something in good condition. 使某物保持良好状态的过程。 Example Sentences: &#xfeff;&#xfeff;• &#xfeff;Regular maintenance of the codebase is essential to keep the application secure …

作者头像 李华