人人都会AI编程

计算属性缓存与避免内联函数

更新时间:2026-07-11

Vue 的模板中经常需要执行一些数据转换或计算,但怎么写这些计算,会直接影响组件的渲染性能。这一节聚焦两个简单却高效的原则:善用计算属性的缓存,以及避免在模板中直接写内联函数。

计算属性的缓存机制

computed 的核心价值不是“可以写复杂逻辑”,而是它会缓存计算结果。只有当计算属性依赖的响应式数据发生变化时,它才会重新计算;否则,无论模板访问多少次,都直接返回上一次的缓存值。

<script setup>
import { ref, computed } from 'vue'

const list = ref([...]) // 一个很长的数组

// computed:依赖 list,只在 list 变化时重新过滤
const filteredList = computed(() => {
  return list.value.filter(item => item.status === 'active')
})
</script>

<template>
  <div v-for="item in filteredList" :key="item.id">
    {{ item.name }}
  </div>
</template>

在上面的代码中,如果模板里多次渲染了 filteredList,或者组件因为其他数据变化而重新渲染,只要 list 没变,filteredList 就不会重新执行。相比直接在模板里写 list.filter(...),计算属性避免了每次组件更新都重复执行同样的计算。

常见误区:用方法替代计算属性

<!-- 反例:每次重新渲染都会执行 filter -->
<div v-for="item in list.filter(i => i.status === 'active')" :key="item.id">
  {{ item.name }}
</div>

表面上结果一样,但 list.filter 会在组件每次更新时都重新执行,即便 list 根本没有变化。如果 list 有几百条数据,组件又频繁更新(比如因为其他响应式数据变动),这种无意义的重复计算就是实打实的性能浪费。

实用建议:只要逻辑在模板中多次使用,或涉及数组的 filtermapsort 等操作,就一定放进 computed。哪怕只用于一处,也能避免不必要的重新计算。

避免模板中的内联函数

在模板里直接写箭头函数,是另一个容易被忽视的性能坑点。

<template>
  <ChildComponent 
    v-for="item in list" 
    :key="item.id"
    :on-click="() => handleClick(item)" 
  />
</template>

() => handleClick(item) 是一个匿名函数。每次父组件重新渲染时,Vue 都会创建一个全新的函数实例传给 ChildComponent。对于 ChildComponent 来说,它接收到的 on-click prop 是一个“新”值(引用不同),即使逻辑完全没变,也会触发子组件的更新。如果子组件内部没有做 shouldComponentUpdate 类的优化,这个无意义的更新就会耗费资源。

更好的写法:利用 method 或组合式函数返回一个稳定的引用。

<script setup>
function createClickHandler(item) {
  return () => handleClick(item)
}
</script>

<template>
  <ChildComponent 
    v-for="item in list" 
    :key="item.id"
    :on-click="createClickHandler(item)" 
  />
</template>

不过请注意:createClickHandler(item) 在每次渲染时也会返回新函数,除非你在 createClickHandler 内部使用缓存(比如 WeakMap 记录每个 item 对应的函数),否则仍然存在同样的引用变化问题。

最优解:尽量让子组件自己处理逻辑,或使用事件参数。

<template>
  <ChildComponent 
    v-for="item in list" 
    :key="item.id"
    :item="item"
    @click="handleClick"
  />
</template>

ChildComponent 内部触发 $emit('click', item),父组件的事件处理函数 handleClick 引用是稳定的。这样就不需要为每个列表项生成新函数,子组件的 props 也不会因为引用变化而被动更新。

总结

  • 计算属性缓存:把模板中重复使用或开销较大的数据转换逻辑放在 computed 里,让 Vue 自动决定是否需要重新计算。
  • 避免内联函数:模板中每次渲染生成的新函数引用会破坏子组件的 Diff 优化,尽量让事件处理保持引用稳定。

这两个习惯几乎零成本,但能有效降低组件不必要的计算和子组件更新,尤其在有长列表或频繁交互的页面中,累积的性能收益非常可观。