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

Vue3 里 ref 和 computed 咋区分?用法、原理、场景全解析

terry 2小时前 阅读数 4 #SEO
文章标签 Vue3;ref computed

ref 在 Vue3 里是干啥用的?

Vue3 里 ref 主要用来给基本数据类型(比如字符串、数字、布尔值)做响应式处理,也能给对象、数组这些引用类型做响应式,举个例子,你想让一个数字 count 变成响应式,就可以写 const count = ref(0),这时候 count 其实是个“响应式对象”,里面藏着真正的数值,要通过 count.value 才能拿到或修改它。

为啥要这么设计?因为 JS 里基本类型是按值传递的,没法直接拦截修改操作,Vue 就搞了个“包装器”,把基本类型包成对象,这样就能通过拦截对象的 value 属性的 getset,来实现数据变化时自动更新视图。

要是给 ref 传对象,const user = ref({name: '小明'}),内部其实会用 reactive 把这个对象变成深层响应式的,不过和直接用 reactive 比,ref 更灵活——它能处理基本类型,而且在组合式 API 里跨组件传递数据时(比如用 provide/inject),ref.value 结构更方便维护。

computed 适合解决哪些场景?

computed 叫“计算属性”,核心是基于其他响应式数据做衍生计算,还能缓存结果,举个电商场景的例子:购物车页面里,商品的单价(price)和数量(num)都是响应式数据,总价 total = price * num,这时候用 computed 来写 total,就会自动跟踪 pricenum 的变化——只要这俩不变,每次访问 total 都直接用缓存的结果,不用重复计算;只有 pricenum 变了,才会重新算总价。

再对比 methods 里的函数:如果把 total 写成 method 里的函数,每次页面渲染或者数据变化时,不管 pricenum 有没有变,函数都会重新执行,要是计算逻辑很复杂(比如遍历大数组、调接口),用 computed 的缓存能省很多性能。

computed 适合这两类场景:一是需要“依赖其他响应式数据做动态计算”,二是计算逻辑要复用且希望性能更优(利用缓存减少重复计算)。

ref 和 computed 核心差异在哪?

得从功能定位、依赖关系、缓存这几个角度拆:

  • 功能定位不同ref 是“响应式数据的容器”,负责把普通数据(不管基本还是引用类型)包成响应式;computed 是“基于已有响应式数据的计算器”,本身不存原始数据,只负责根据依赖生成新数据。
  • 依赖关系不同ref 自己就是“数据源”,修改 ref.value 会触发视图更新;computed 的结果完全依赖其他响应式数据refreactive 里的数据),它自己没法主动修改,只能被动跟着依赖变。
  • 缓存机制不同ref 没有缓存逻辑,每次修改 .value 都会触发更新;computed 有缓存,只有依赖的响应式数据变了,才会重新计算,否则重复访问都用缓存结果。

实际开发选 ref 还是 computed?要避开哪些坑?

选的时候看需求:

  • 要是需要单独存一个响应式数据(比如用户输入的表单值、组件内的开关状态),用 ref,比如搜索框的输入内容:const searchValue = ref(''),输入时改 searchValue.value,视图自动更。
  • 要是需要基于已有数据做计算,还想省性能,用 computed,比如文章列表里,根据筛选条件(isShow)和原始列表(list),生成过滤后的列表 filteredList,就可以写 const filteredList = computed(() => list.filter(item => item.isShow))

踩坑点得注意这几个:

  • ref 在 JS 里修改必须带 .value,模板里不用,比如在 setup 里写 count.value++ 是对的,但模板里直接 {{ count }} 就行,Vue 会自动解包,要是忘写 .value,在 JS 里改基本类型就没法触发响应式更新,新手很容易犯这错。
  • computed 里别写异步逻辑,因为 computed 要返回计算后的值,要是里面用 async/await,返回的是 Promise,模板里渲染会变成 [object Promise],没法正常显示,这种情况得用 watch 或者单独的异步函数处理。
  • 别把 computedmethod 用,比如明明只需要简单的拼接字符串,却用 computed 包一层,反而多了不必要的依赖跟踪,这种场景直接用 method 或者直接在模板里写表达式更简单。

从原理看 ref 和 computed 为啥不一样?

得从 Vue3 的响应式系统底层逻辑讲:

  • ref 的响应式原理:对于基本类型,ref 创建了一个带 value 属性的对象,通过拦截 value 的读取和修改来实现响应式(Vue3 里对基本类型用类似 Object.defineProperty 的逻辑包装,对对象则内部调用 reactive),每次读 value 时收集依赖(比如哪个组件在用这个值),每次改 value 时触发依赖更新(通知组件重新渲染)。
  • computed 的响应式原理computed 内部有个“dirty”标记,默认是 true,第一次访问 computed 的值时,会执行计算函数,把结果缓存,然后把 dirty 设为 false,之后如果依赖的响应式数据(比如某个 refreactive 里的属性)变化了,Vue 会把 dirty 设为 true,下一次访问 computed 的值时,先检查 dirty:如果是 true,就重新执行计算函数,更新缓存,再把 dirty 设为 false;如果是 false,直接返回缓存的结果,这样就实现了“依赖变化才重新计算”的缓存逻辑。

简单总结下:ref 是响应式的“载体”,负责让数据能被 Vue 跟踪变化;computed 是响应式的“计算器”,负责基于已有数据做高效计算,开发时先想清楚需求:存数据用 ref,算数据用 computed,避开 .value 和异步这些坑,代码既高效又不容易出 bug~

版权声明

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

热门