Vue 3.5 下一站:cached 提案,重新定义响应式缓存
在 Vue 3.5 正式发布后,Vue 团队又放出了一个令人兴奋的 RFC(征求意见稿):
cached函数 。它旨在解决computed与普通函数之间"性能与简洁"难以两全的问题。本文带你抢先看懂这个新特性。
场景:我们为什么需要 cached?
在日常开发中,经常会写这样的代码:
vue
<script setup>
import { computed } from 'vue'
const props = defineProps(['items', 'filterText'])
// 需求:过滤列表,但只在依赖变化时重新计算
const filtered = computed(() =>
props.items.filter(item => item.includes(props.filterText))
)
</script>
computed 很完美:自动缓存,依赖变化才重算。但有时候我们只是想在模板或 watch 中执行一个代价高昂但不需要缓存 的计算,又或者想控制缓存的粒度。目前的 computed 强制缓存且无法手动清除,而普通函数又完全没有缓存。
cached 就是为了填补这个空白。
cached 的提案 API
根据 RFC,cached 的用法如下:
javascript
import { cached } from 'vue'
const expensiveCalc = cached((a, b) => {
console.log('计算中...')
return a + b
})
expensiveCalc(1, 2) // 输出 '计算中...',返回 3
expensiveCalc(1, 2) // 命中缓存,不输出,直接返回 3
expensiveCalc(2, 3) // 参数变化,重新计算
与 computed 不同:
cached接收一个普通函数,返回一个带有缓存的函数。- 缓存键基于传入的所有参数(浅比较)。
- 没有响应式依赖自动追踪,完全显式控制。
在 Vue 组件中的实际应用
vue
<script setup>
import { cached, ref } from 'vue'
const searchKeyword = ref('')
const users = ref([{ name: 'Alice' }, { name: 'Bob' }])
// 根据关键词过滤用户,成本较高(假设有复杂逻辑)
const filterUsers = cached((keyword, userList) => {
console.log('执行过滤')
return userList.filter(u => u.name.includes(keyword))
})
// 在 watch 或模板中使用
watch([searchKeyword, users], () => {
const result = filterUsers(searchKeyword.value, users.value)
console.log(result)
})
</script>
当 searchKeyword 频繁变化且 users 未变时,cached 能避免重复计算;但如果只想在真正需要时重新计算,cached 比 computed 更轻量,因为不需要建立响应式依赖图。
cached vs computed 对比
| 特性 | computed |
cached(提案) |
|---|---|---|
| 依赖追踪 | 自动追踪响应式依赖 | 无,依赖显式传参 |
| 缓存策略 | 依赖变化时自动失效 | 参数浅比较,命中则返回 |
| 适用场景 | 衍生状态,自动更新 | 昂贵且参数可缓存的纯函数 |
| 手动清除缓存 | ❌ 不支持 | ✅ 可设计 clear 方法(提案中) |
潜在用例
- 格式化大对象 :
cached(formatBigData, data),避免重复深拷贝。 - API 请求去重:相同参数短时间内不重复请求。
- 复杂列表排序:只在排序依据变化时重新排序。
当前状态与展望
cached 目前处于早期 RFC 阶段 ,尚未合并到主分支。Vue 团队希望在 3.6 或 3.7 中引入。如果你对这个功能有想法,可以去 GitHub 的 vuejs/rfcs 仓库参与讨论。
小结
cached 提供了一种轻量级、显式参数的缓存机制,填补了 computed 和普通函数之间的空白。它不是要取代 computed,而是给开发者更多选择。一旦正式发布,将成为 Vue 响应式工具箱中的又一把利器。
我会持续关注 RFC 的进展,等正式发布时再出一篇深度解析。如果你对
cached的实现原理(基于WeakMap的 LRU 缓存)感兴趣,留言告诉我,下次安排!