Code前端首页关于Code前端联系我们

Vue3中computed和ref该咋用?常见疑问一次说清

terry 21小时前 阅读数 138 #SEO
文章标签 Vue3;computed/ref

ref是用来做什么的?和reactive有啥区别?

在Vue3组合式API里,ref让基本数据类型(数字、字符串、布尔值等)也能变成响应式的核心工具,比如写 const count = ref(0) ,Vue会给count包一层“壳”(变成Ref对象),实际数值存在.value里,在<script setup>里读写时得用count.value,但模板里不用写.value,Vue会自动“解包”。

reactive的区别得从适用场景看:

  • reactive更适合处理对象/数组这类复杂数据,而且是“深层响应式”——对象里嵌套的属性变化也能被检测到,但它有个限制:不能直接给整个对象重新赋值,否则会丢失响应式连接(比如user = { name: '新名' }就会失效,得用user.name = '新名')。
  • ref不管是基本类型还是对象,都能通过.value灵活赋值(比如count.value = 1userRef.value = { name: '新名' }都没问题),所以日常开发里,存单个基础值优先用ref,存复杂对象/数组可用reactive,不过ref存对象也很常见,看个人习惯~

computed计算属性要怎么用?为啥非得用它?

computed主要用来处理“衍生数据”——依赖其他响应式数据算出来的值,比如购物车商品的总价(每个商品价格×数量再求和)、用户全名(姓+名)、过滤后的列表等场景,用computed特别合适。

它的用法分两种:

  • 函数式:直接写计算逻辑,const total = computed(() => 依赖数据的计算逻辑)
  • 对象式:带getset(少见,多用于双向绑定场景)。

核心优势是缓存机制:只有依赖的响应式数据变化时,才会重新计算;依赖不变时,多次访问结果都是缓存好的,性能比methods里的函数高很多(methods每次调用都要重新执行)。

举个实际代码例子感受下:

<template>
  <div>
    商品1价格:<input v-model.number="price1" /> 数量:<input v-model.number="num1" />
    商品2价格:<input v-model.number="price2" /> 数量:<input v-model.number="num2" />
    总价:{{ totalPrice }}
  </div>
</template>
<script setup>
import { ref, computed } from 'vue'
const price1 = ref(10)
const num1 = ref(2)
const price2 = ref(15)
const num2 = ref(3)
// 计算总价,依赖4个ref的数据
const totalPrice = computed(() => {
  return price1.value * num1.value + price2.value * num2.value
})
</script>

只要price1num1这些依赖的ref值变化,totalPrice会自动更新;依赖不变时,重复读totalPrice不会重复计算,要是换成methods里的函数,每次渲染或调用都得重新算,数据多了就会变慢,所以遇到“依赖其他数据的衍生值”场景,优先用computed

computed和ref在响应式处理上有啥关联?

computed能自动追踪依赖的响应式数据,而这些数据往往是用ref(或reactive)声明的,比如上面例子里,totalPrice依赖的price1num1都是ref对象,Vue的响应式系统会在computed执行时,记录“哪些ref/reactive的数据被用了”;之后只要这些数据的.value(或属性)变化,computed就会重新计算。

再看个简单关联案例:

const count = ref(1)
const double = computed(() => count.value * 2)
console.log(double.value) // 输出2
count.value = 2
console.log(double.value) // 输出4(count变化触发double重新计算)

这里doublecomputed,依赖的是count这个refcount.value一变,double就自动更新,可以理解为:ref是“原料容器”,computed是“加工厂”——加工厂把原料容器里的内容当输入,加工出新的响应式结果computed返回的也是Ref对象,模板里用的时候不用.value)。

反过来,没必要用refcomputed的结果(比如别写const doubleRef = ref(double.value)),因为computed本身已经是响应式的,这么做会让doubleRef失去自动更新的能力,浪费computed的设计意义。

啥时候该用ref存数据,啥时候用computed做计算?

分场景判断更清晰:

ref的场景

  • 单个基础数据(如年龄、开关状态、输入框即时值):比如const age = ref(18)const isShow = ref(false)
  • 需要单独控制的对象/数组:比如用户信息对象(后续可能整个替换)、待办列表数组。
  • 作为组件内部的“临时状态”:比如弹窗显示隐藏、表单输入的中间值。

computed的场景

  • 需要基于其他响应式数据计算的值:比如拼接姓名(firstName + lastName)、过滤列表(从大数组筛出符合条件的项)、计算总和/平均值。
  • 希望缓存计算结果,避免重复执行耗时逻辑(比如遍历大数组做统计)。
  • 处理“依赖变化才更新”的逻辑:比如购物车总价、分页的当前页数据切片。

举个场景对比的例子:做搜索功能时,输入框内容用ref存(const searchKey = ref('')),列表数据是另一个refconst list = ref([...])),这时候“过滤后的列表”适合用computed

const filteredList = computed(() => {
  return list.value.filter(item => item.name.includes(searchKey.value))
})

只要searchKeylist变化,filteredList自动更新;没变化时不会重复过滤,性能更好,要是把过滤逻辑放methods里,每次输入都得重新过滤整个列表,效率很低,所以核心判断点是:数据是“原始存储”还是“衍生计算”,衍生计算优先用computed

用computed和ref时容易踩的坑有哪些?

这俩API看似简单,实际开发中容易犯这些错:

ref的常见坑

  1. 忘记写.value:在<script setup>里操作ref时,必须用.value读写!比如count.value++是对的,写count++就错了(因为count是Ref对象,不是原始值),但模板里不用写.value,Vue会自动解包,别搞混。
  2. ref和普通变量搞混:比如声明let count = 0(普通变量),想让它响应式却没包ref,之后修改count页面不更新——因为普通变量不是响应式的,Vue检测不到变化。
  3. ref存的对象重新赋值时误解响应式:比如const user = ref({ name: '张三' }),直接user.value = { name: '李四' }是没问题的(新对象仍会被ref处理成响应式);但如果用reactive声明const user = reactive({ name: '张三' }),再写user = { name: '李四' }就会丢失响应式(reactive不能直接整个赋值),所以用ref存对象更灵活,但逻辑要理清楚。

computed的常见坑

  1. computed里直接修改依赖数据:比如const fullName = computed(() => { firstName.value = '强制修改'; return firstName.value + lastName.value })——这会导致循环更新,还违背computed“只做计算、不做修改”的设计。computed应该是纯函数,修改操作要放到methodswatch里。
  2. 依赖了非响应式数据:比如const num = 1; const double = computed(() => num * 2)——num是普通变量,不是ref/reactive,所以num变化时double不会更新,得把num包成refconst num = ref(1))才行。
  3. computed做异步操作computed的函数里不能写异步(比如async/await),因为它要返回确定的值,而异步函数返回的是Promise,这种情况得用watch或单独的函数处理异步,再把结果存到ref里。

举个错误示范和修正:
错误:在computed里改数据

const firstName = ref('张')
const lastName = ref('三')
const fullName = computed(() => {
  // 错误:computed里修改依赖的ref
  firstName.value = '李' 
  return firstName.value + lastName.value
})

修正:把修改逻辑放到methodscomputed只做计算

const firstName = ref('张')
const lastName = ref('三')
const fullName = computed(() => firstName.value + lastName.value)
function changeFirstName() {
  firstName.value = '李' // 正确:修改操作放外面
}

ref是Vue3创建响应式数据的基础工具,适合存原始值或需单独控制的对象;computed是基于依赖的“智能计算器”,适合处理衍生数据和性能优化,理清它们的角色和场景,写代码时就不容易混淆,还能避开响应式失效的坑~

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

热门