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

Vue3里computed和ref有啥区别?该咋选?

terry 2天前 阅读数 23 #SEO
文章标签 computed;ref

日常写Vue3项目时,不少同学会纠结:“同样是响应式,ref和computed到底咋选?它们有啥本质区别?”今天咱们从功能、原理、场景这些角度拆解清楚,以后写代码不纠结~

先搞懂ref是干啥的?

ref是Vue3里最基础的响应式数据容器,不管你要存字符串、数字这类“基本类型”,还是对象、数组这些“引用类型”,用ref包一层,就能让数据变成“响应式”——意思是数据变了,页面能自动更新,其他依赖这个数据的逻辑也会跟着变。

举个栗子🌰:
做个登录页,用户名输入框的数据得是响应式的吧?用ref就很合适:

const username = ref('') 
// 模板里用 v-model="username" ,输入时username.value会自动更新

再比如做个弹窗,控制显示隐藏的开关:

const isDialogShow = ref(false) 
// 点击“打开弹窗”按钮时,执行 isDialogShow.value = true

简单说,ref就像个“智能储物盒”:你往里面存数据(不管是简单值还是复杂对象),它帮你盯紧数据变化,一旦变了就通知Vue更新页面、触发依赖逻辑。

computed又是什么逻辑?

computed叫计算属性,它的核心是“基于已有响应式数据,推导新结果,还带缓存”。

啥意思?比如你有用户的“姓”和“名”两个响应式数据(用ref存的),现在要显示“全名”,如果每次用全名都手动拼接,既麻烦又容易漏更新,这时候用computed,它会自动监听依赖(姓和名),依赖变了才重新计算,没变就复用之前的结果(缓存)。

看例子🌰:

const firstName = ref('张') 
const lastName = ref('三') 
// 用computed推导全名
const fullName = computed(() => {
  return `${firstName.value} ${lastName.value}`
})

模板里用{{ fullName }},当firstName或lastName变化时,fullName会自动更新;如果两个都没变化,哪怕模板里多次用到fullName,computed也不会重复执行函数,直接拿缓存的结果,性能更优。

再举个复杂点的场景:购物车计算总价,商品列表是响应式数组(ref包的),每个商品有price(价格)和count(数量):

const cartItems = ref([
  { id: 1, price: 10, count: 2 },
  { id: 2, price: 20, count: 1 }
])
// 计算总价:每个商品的价格×数量相加
const totalPrice = computed(() => {
  return cartItems.value.reduce((sum, item) => {
    return sum + item.price * item.count
  }, 0)
})

这时,只有当cartItems里的商品数量、价格变化时,totalPrice才会重新计算;如果商品列表没动,多次访问totalPrice都是直接拿缓存值,不用重复遍历数组求和,省性能。

核心区别到底在哪?

现在你大概有感觉了:ref是“存数据”的,computed是“加工数据”的,但具体差异得拆细了看——

功能定位:存储 vs 计算

  • ref是“数据容器”:负责存原始数据(不管简单还是复杂),是响应式的“基石”,你要存个开关状态、用户输入、列表数据?选ref。
  • computed是“数据加工厂”:本身不存原始数据,而是基于其他响应式数据(比如ref、reactive里的数据)推导新值,你要把多个数据拼起来、做复杂计算、格式化数据?选computed。

响应式触发逻辑:主动改 vs 自动跟

  • ref的更新逻辑:你主动修改ref的.value,触发响应式更新,比如count.value++,Vue会检测到count变化,然后更新所有用到count的地方。
  • computed的更新逻辑:依赖的数据变了,computed自动更新,比如fullName依赖firstName和lastName,只有这俩变了,fullName才会重新计算;如果依赖没动,computed不会执行函数,直接返回缓存结果。

使用场景:啥时候用哪个?

举几个真实开发场景,你就明白边界在哪了:

场景描述 该用ref还是computed? 为啥?
存储用户输入的搜索关键词 ref 搜索关键词是“原始数据”,需要直接存储、修改
把姓和名拼成全名显示 computed 全名是“推导结果”,依赖姓和名,且需要缓存
购物车计算所有商品总价 computed 总价依赖商品列表的价格和数量,复杂计算需要缓存
控制弹窗显示隐藏的开关 ref 开关状态是“原始数据”,需要直接修改
把后台返回的时间戳格式化成“YYYY - MM - DD” computed 格式化后时间是“推导结果”,依赖原始时间戳

语法与可写性:能不能直接改?

  • ref:可读可写,通过.value修改数据,比如count.value = 10
  • computed:默认只读,你不能直接改fullName.value(会报错);如果非要“可写”,得给computed传一个包含getset的对象,在set里修改依赖的响应式数据。
    const fullName = computed({
      get() { return `${first.value} ${last.value}` },
      set(newVal) { 
        // 假设newVal是“张 三”,拆分后修改first和last
        const [f, l] = newVal.split(' ')
        first.value = f
        last.value = l
      }
    })
    // 这时可以执行 fullName.value = "李 四" ,会触发first和last的更新

    这种“可写computed”场景很少,一般用在表单双向绑定需要同时改多个字段的情况(比如全名输入框同时改姓和名)。

实战中容易踩的坑和误解

理解了区别,还要避开这些常见错误:

误解1:“computed能替代ref存数据”

不行!computed必须依赖其他响应式数据才能工作,比如你想存一个初始值为0的计数器,必须用ref:const count = ref(0);要是用computed,没依赖的话函数只执行一次就缓存,根本没法主动修改值。

误解2:“ref的计算不需要缓存,所以性能差”

不是的~ref是“存储数据”,每次访问的是当前值,修改时触发更新;computed是“计算数据”,有缓存是为了避免重复计算(比如复杂遍历、逻辑判断),两者设计目的不同,不存在谁性能更好,只看场景对不对。

坑1:在computed里写异步代码

computed的getter必须同步返回值,因为要立即拿到结果存缓存,如果要处理异步(比如调接口拿数据再计算),得用watch或者onMounted这类生命周期 + ref的组合,别硬往computed里塞异步。

坑2:直接修改computed的.value

默认情况下,computed返回的是只读ref,强行改fullName.value = 'xxx'会报错,如果业务需要“可写”,必须按上面说的配置set函数,且在set里修改依赖的响应式数据。

选ref还是computed?

记住一句话:“存数据用ref,算结果用computed”

  • 当你需要一个“响应式的容器”来存原始数据(不管是简单值还是对象),并且要能主动修改它 → 选ref。
  • 当你需要基于已有响应式数据做“推导/计算”,并且希望结果能缓存(避免重复计算) → 选computed。

举个🌰加深记忆:做 TodoList 时,“待办事项列表”是原始数据 → 用ref存;“已完成事项的数量”是推导结果(依赖待办列表里每个项的完成状态) → 用computed算。

分清这层逻辑,写Vue3代码时就不会再纠结“该用ref还是computed”啦~

版权声明

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

热门