4、Vue3 中 computed 计算属性和方法的区别详解
·
目录
一、computed 计算属性的工作原理与用途
1. 工作原理
响应式依赖追踪机制:
import { ref, computed } from 'vue'
const firstName = ref('张')
const lastName = ref('三')
// computed 内部会追踪 firstName 和 lastName
const fullName = computed(() => {
console.log('computed 执行了')
return firstName.value + lastName.value
})
核心原理:
- 依赖收集(Dependency Collection): 首次执行时,会收集函数内部访问的所有响应式数据
- 缓存机制(Caching): 计算结果会被缓存,只有依赖变化时才重新计算
- 惰性求值(Lazy Evaluation): 只有被访问时才会执行计算
- 内部标识: 维护
_dirty标志位,标记是否需要重新计算
底层实现简化版:
class ComputedRefImpl {
constructor(getter) {
this._getter = getter
this._dirty = true // 脏标记
this._value = undefined
}
get value() {
// 只有 dirty 为 true 时才重新计算
if (this._dirty) {
this._value = this._getter()
this._dirty = false
}
return this._value
}
}
2. 主要用途
- 衍生状态: 用于根据现有数据计算出新的数据(如:过滤列表、总价计算、格式化字符串)。
- 性能优化: 处理需要大量计算逻辑的代码,避免在每次渲染时重复计算。
典型应用场景:
// 1. 列表过滤
const filteredList = computed(() => {
return list.value.filter(item => item.status === 'active')
})
// 2. 总价计算
const totalPrice = computed(() => {
return cart.value.reduce((sum, item) => sum + item.price * item.quantity, 0)
})
// 3. 表单验证
const isFormValid = computed(() => {
return username.value.length >= 3 && password.value.length >= 6
})
二、方法(methods)的工作原理与用途
1. 工作原理
- 普通函数执行:
methods本质上就是普通的 JavaScript 函数。 - 无缓存: 每当组件重新渲染(Re-render)时,或者在模板中被调用时,
methods总会重新执行函数体。 - 主动调用: 它不会自动追踪依赖,必须被显式调用(如在模板中
getName()或在事件处理中调用)。
export default {
setup() {
const count = ref(0)
// 普通函数,每次调用都会执行
const getDouble = () => {
console.log('method 执行了')
return count.value * 2
}
return { count, getDouble }
}
}
2. 主要用途
- 事件监听: 处理 DOM 事件(点击、提交等)。
- 传参逻辑: 当你需要根据传入的参数返回不同的结果时(计算属性不能直接传参,除非返回一个函数)。
- 非响应式操作: 如发起 API 请求、定时器操作或修改数据状态。
典型应用场景:
// 1. 事件处理
const handleSubmit = () => {
// 提交表单逻辑
api.submitForm(formData.value)
}
// 2. 带参数的方法
const formatPrice = (price, currency = '¥') => {
return `${currency}${price.toFixed(2)}`
}
// 3. 异步操作
const fetchData = async () => {
loading.value = true
const res = await api.getData()
data.value = res
loading.value = false
}
三、具体区别(深度对比)
1. 核心差异表
| 维度 | Computed 计算属性 | Methods 方法 |
|---|---|---|
| 缓存机制 | ✅ 有缓存,依赖不变不重新计算 | ❌ 无缓存,每次调用都执行 |
| 调用方式 | 像属性访问:fullName |
像函数调用:getName() |
| 依赖追踪 | ✅ 自动追踪响应式依赖 | ❌ 不追踪依赖 |
| 传参能力 | ❌ 不支持直接传参 | ✅ 支持任意参数 |
| 执行时机 | 依赖变化 + 被访问时 | 被调用时 |
| 返回值 | ComputedRef 对象 | 普通函数返回值 |
| 副作用 | ❌ 不应有副作用(纯函数) | ✅ 可以有副作用 |
| 性能 | 高(有缓存) | 相对较低(无缓存) |
2. 性能对比示例
import { ref, computed } from 'vue'
export default {
setup() {
const count = ref(0)
const other = ref(0)
// Computed:只有 count 变化才重新计算
const doubleComputed = computed(() => {
console.log('computed 执行')
return count.value * 2
})
// Method:每次渲染都执行
const doubleMethod = () => {
console.log('method 执行')
return count.value * 2
}
// 改变 other,触发重新渲染
const changeOther = () => {
other.value++
// computed 不会执行(依赖未变)
// 但模板中的 doubleMethod() 会执行
}
return {
count,
other,
doubleComputed,
doubleMethod,
changeOther
}
}
}
模板中的表现:
<template>
<div>
<!-- 访问 computed,有缓存 -->
<p>Computed: {{ doubleComputed }}</p>
<p>Computed: {{ doubleComputed }}</p> <!-- 不会重复计算 -->
<!-- 调用 method,无缓存 -->
<p>Method: {{ doubleMethod() }}</p>
<p>Method: {{ doubleMethod() }}</p> <!-- 会重复执行 -->
<button @click="changeOther">改变 other</button>
<!-- 点击后,method 会执行,computed 不会 -->
</div>
</template>
3. 特殊场景对比
场景1:需要传参
// ❌ Computed 不能直接传参
const getUser = computed((id) => { // 错误!
return users.value.find(u => u.id === id)
})
// ✅ 可以返回函数(闭包)
const getUserById = computed(() => {
return (id) => users.value.find(u => u.id === id)
})
// 使用:getUserById.value(1)
// ✅ Method 直接支持
const getUser = (id) => {
return users.value.find(u => u.id === id)
}
// 使用:getUser(1)
场景2:副作用操作
// ❌ Computed 不应有副作用
const badComputed = computed(() => {
console.log('执行了') // 副作用
api.logEvent() // 副作用
return count.value * 2
})
// ✅ Method 可以有副作用
const goodMethod = () => {
console.log('执行了')
api.logEvent()
return count.value * 2
}
四、面试怎么回答才精彩
🎯 标准回答框架(STAR 法则)
第一层:核心概念(30秒)
"computed 和 methods 最本质的区别是缓存机制。computed 基于响应式依赖进行缓存,只有依赖变化时才重新计算;而 methods 每次调用都会执行,没有缓存。"
第二层:原理深度(1分钟)
"从实现原理来看:
Computed 的核心机制:
- 内部维护一个
_dirty标志位和_value缓存- 首次访问时执行 getter 并收集依赖
- 依赖变化时只标记 dirty 为 true,不立即计算
- 下次访问时才真正重新计算(惰性求值)
Methods 的特点:
- 就是普通的 JavaScript 函数
- 不参与响应式系统
- 每次调用都是全新执行"
第三层:场景选择(体现经验)
"在实际开发中,我的选择标准是:
优先使用 Computed:
- ✅ 数据转换、格式化(如价格、日期)
- ✅ 列表过滤、排序
- ✅ 多个状态的组合计算
- ✅ 表单验证状态
必须使用 Methods:
- ✅ 事件处理(@click、@submit)
- ✅ 需要传参的动态查询
- ✅ 异步操作(API 请求)
- ✅ 有副作用的操作(修改数据、日志记录)"
第四层:性能优化(加分项)
"从性能角度:
Computed 的优势:
// 假设有 1000 个商品需要过滤 const expensiveList = computed(() => { console.log('执行了昂贵的计算') return products.value.filter(p => p.price > 1000) }) // 在模板中多次使用,只计算一次实际案例:
我曾优化过一个商品列表页面,原本在 methods 中做过滤,每次组件更新都会重新过滤 5000+ 条数据。改用 computed 后,只有筛选条件变化时才重新计算,页面流畅度提升明显。"
第五层:Vue 3 新特性(进阶)
"在 Vue 3 Composition API 中:
- Computed 返回
ComputedRef<T>类型,是只读的- 可以配置 getter 和 setter,但不推荐滥用
- 配合 TypeScript 有更好的类型推导
// 可写的 computed const fullName = computed({ get: () => `${firstName.value} ${lastName.value}`, set: (val) => { const [first, last] = val.split(' ') firstName.value = first lastName.value = last } }) ```"
🌟 面试金句(记住这些)
-
核心差异:
"Computed 是以空间换时间(缓存结果),Methods 是以时间换空间(每次计算)"
-
使用原则:
"能用 computed 就用 computed,需要灵活性才用 methods"
-
性能优化:
"在处理大数据量或复杂计算时,合理使用 computed 的缓存特性是 Vue 性能优化的关键手段"
-
设计哲学:
"Computed 体现了声明式编程思想,关注'是什么';Methods 体现了命令式编程,关注'怎么做'"
📝 完整示例代码
import { ref, computed, watch } from 'vue'
export default {
setup() {
// 响应式数据
const products = ref([
{ id: 1, name: 'iPhone', price: 6999, category: 'phone' },
{ id: 2, name: 'iPad', price: 3999, category: 'tablet' },
{ id: 3, name: 'MacBook', price: 9999, category: 'laptop' }
])
const searchText = ref('')
const selectedCategory = ref('all')
// ✅ Computed:数据过滤(有缓存)
const filteredProducts = computed(() => {
console.log('computed 执行了')
let result = products.value
// 按分类过滤
if (selectedCategory.value !== 'all') {
result = result.filter(p => p.category === selectedCategory.value)
}
// 按搜索文本过滤
if (searchText.value) {
result = result.filter(p =>
p.name.toLowerCase().includes(searchText.value.toLowerCase())
)
}
return result
})
// ✅ Computed:总价计算
const totalPrice = computed(() => {
return filteredProducts.value.reduce((sum, p) => sum + p.price, 0)
})
// ✅ Method:格式化价格(需要传参)
const formatPrice = (price, showSymbol = true) => {
const formatted = price.toLocaleString('zh-CN')
return showSymbol ? `¥${formatted}` : formatted
}
// ✅ Method:添加商品(有副作用)
const addProduct = (product) => {
products.value.push({
...product,
id: Date.now()
})
// 副作用:发送统计
analytics.track('product_added')
}
// ✅ Method:异步加载
const loadProducts = async () => {
const data = await api.getProducts()
products.value = data
}
return {
products,
searchText,
selectedCategory,
filteredProducts, // computed
totalPrice, // computed
formatPrice, // method
addProduct, // method
loadProducts // method
}
}
}
关键要点总结:
- Computed 用于纯计算,无副作用,有缓存
- Methods 用于业务逻辑,可有副作用,无缓存
- 性能敏感场景优先 computed
- 需要灵活性场景使用 methods
更多推荐



所有评论(0)