Vue 的 computed 计算属性之所以高效,核心在于其内置的缓存机制。只有当依赖项发生变化时,它才会重新计算,否则直接返回上一次的结果。理解缓存失效的条件,是优化 Vue 应用性能的关键。
以下是基于 Vue 响应式原理的缓存失效判定规则与实操指南。
一、 核心判定机制:依赖变化
计算属性的缓存失效严格遵循“依赖收集”原则。Vue 会自动追踪计算属性 getter 函数中访问的响应式依赖。只有当这些依赖的值发生改变时,缓存才会失效。
判定流程如下:
具体执行步骤:
- 访问 计算属性时,Vue 检查 该属性的
dirty标记位。 - 若
dirty为true(脏),执行getter函数进行重新计算,更新 缓存,并将dirty置为false。 - 若
dirty为false(干净),直接返回 缓存值,跳过 计算过程。
二、 必然触发失效的场景
以下情况会强制将 dirty 标记置为 true,导致下次访问时重新计算。
1. 响应式数据值改变
这是最基础的失效条件。计算属性内部引用的 data、props 或 state 发生变化。
代码示例:
export default {
data() {
return {
message: 'Hello'
}
},
computed: {
reversedMessage() {
// 依赖项:this.message
return this.message.split('').reverse().join('')
}
},
methods: {
update() {
// **修改** 响应式数据
this.message = 'World';
// 此时 reversedMessage 的缓存失效,下次访问将重新计算
}
}
}
2. 响应式对象属性新增或删除
Vue 无法检测到普通对象属性的动态添加或删除,但如果使用 Vue.set (Vue 2) 或直接赋值触发 Proxy 拦截 (Vue 3),缓存同样会失效。
操作步骤:
- 定义 一个包含对象的计算属性。
- 使用
Vue.set添加 新属性,或 使用delete删除 属性(Vue 3 中直接操作即可)。 - 观察 计算属性,其缓存已失效并触发重新渲染。
三、 不触发失效的“陷阱”场景
许多开发者误以为以下情况会触发重新计算,实际上缓存依然有效,这是性能优化的关键点。
1. 依赖对象引用未变,仅内部非响应式属性变化
如果在计算属性中依赖了一个对象,但该对象的内部变化未被 Vue 的响应式系统捕捉到,缓存不会失效。
代码示例(Vue 2 风险点):
export default {
data() {
return {
user: { name: 'Jack' }
}
},
computed: {
userName() {
return this.user.name;
}
},
methods: {
wrongUpdate() {
// **错误方式**:直接添加新属性(Vue 2 中非响应式)
this.user.age = 20;
// userName 的缓存不会失效,因为 user 对象引用未变
}
}
}
2. 依赖数组长度不变,通过索引修改元素(Vue 2)
在 Vue 2 中,直接通过索引修改数组项(如 this.list[0] = 'new')是非响应式的,不会导致依赖该数组的计算属性缓存失效。
解决方案:
- 使用
this.$set(this.list, 0, 'new')。 - 使用
splice方法。
3. 非响应式变量变化
计算属性中如果读取了非 data 中定义的变量(如全局变量、外部引入的常量),这些变量的变化完全无法触发缓存失效。
操作验证:
- 在 组件外部 定义 一个变量
let externalVar = 1。 - 在 计算属性中 返回
externalVar。 - 修改
externalVar的值。 - 查看 页面视图,数据未更新,计算属性未执行。
四、 Vue 3 中的特殊差异
Vue 3 使用 Proxy 实现响应式,解决了很多 Vue 2 的缓存“盲区”,但仍有细节需注意。
1. 对象属性的添加/删除
在 Vue 3 中,Proxy 可以拦截属性的添加和删除。因此,任意 属性的增删都会触发依赖该对象的计算属性缓存失效。
2. Map/Set 数据结构
Vue 3 能够侦测 Map 和 Set 的变化(如 set.add(), map.set())。
操作步骤:
- 创建 一个
ref包装的Set对象。 - 编写 计算属性返回该
Set的大小。 - 调用
value.add()添加 新元素。 - 确认 计算属性缓存失效,自动返回新的大小。
五、 缓存失效判定速查表
为了便于快速判断,请参考以下对照表:
| 场景描述 | Vue 2 表现 | Vue 3 表现 | 是否失效 |
|---|---|---|---|
data 中的基础类型值改变 |
失效 | 失效 | 是 |
props 传入的值改变 |
失效 | 失效 | 是 |
| 对象已存在属性的值改变 | 失效 | 失效 | 是 |
| 对象新增属性 (普通赋值) | 有效 (陷阱) | 失效 | 否 (Vue 2) / 是 (Vue 3) |
| 数组通过索引修改元素 | 有效 (陷阱) | 失效 | 否 (Vue 2) / 是 (Vue 3) |
计算属性返回 Date.now() |
有效 | 有效 | 否 (除非触发重渲染) |
依赖 Vuex/Pinia 的 state |
失效 | 失效 | 是 |
六、 实操验证技巧
在开发过程中,若需确认计算属性是否因缓存失效而重新计算,可采用以下步骤:
- 打开 浏览器开发者工具。
- 在 计算属性的
getter函数内部 添加console.log('computed')。 - 触发 相关数据变化。
- 检查 控制台输出。
- 若 出现 日志,说明缓存失效并重新计算。
- 若 未出现 日志,说明使用了缓存。
代码示例:
computed: {
expensiveOperation() {
console.log('计算执行了'); // 监控点
// 复杂计算逻辑...
return this.value * 2;
}
}
暂无评论,快来抢沙发吧!