Vue3 里 ref 和 computed 咋区分?用法、原理、场景全解析
ref 在 Vue3 里是干啥用的?
Vue3 里 ref 主要用来给基本数据类型(比如字符串、数字、布尔值)做响应式处理,也能给对象、数组这些引用类型做响应式,举个例子,你想让一个数字 count 变成响应式,就可以写 const count = ref(0),这时候 count 其实是个“响应式对象”,里面藏着真正的数值,要通过 count.value 才能拿到或修改它。
为啥要这么设计?因为 JS 里基本类型是按值传递的,没法直接拦截修改操作,Vue 就搞了个“包装器”,把基本类型包成对象,这样就能通过拦截对象的 value 属性的 get 和 set,来实现数据变化时自动更新视图。
要是给 ref 传对象,const user = ref({name: '小明'}),内部其实会用 reactive 把这个对象变成深层响应式的,不过和直接用 reactive 比,ref 更灵活——它能处理基本类型,而且在组合式 API 里跨组件传递数据时(比如用 provide/inject),ref 的 .value 结构更方便维护。
computed 适合解决哪些场景?
computed 叫“计算属性”,核心是基于其他响应式数据做衍生计算,还能缓存结果,举个电商场景的例子:购物车页面里,商品的单价(price)和数量(num)都是响应式数据,总价 total = price * num,这时候用 computed 来写 total,就会自动跟踪 price 和 num 的变化——只要这俩不变,每次访问 total 都直接用缓存的结果,不用重复计算;只有 price 或 num 变了,才会重新算总价。
再对比 methods 里的函数:如果把 total 写成 method 里的函数,每次页面渲染或者数据变化时,不管 price 和 num 有没有变,函数都会重新执行,要是计算逻辑很复杂(比如遍历大数组、调接口),用 computed 的缓存能省很多性能。
computed 适合这两类场景:一是需要“依赖其他响应式数据做动态计算”,二是计算逻辑要复用且希望性能更优(利用缓存减少重复计算)。
ref 和 computed 核心差异在哪?
得从功能定位、依赖关系、缓存这几个角度拆:
- 功能定位不同:
ref是“响应式数据的容器”,负责把普通数据(不管基本还是引用类型)包成响应式;computed是“基于已有响应式数据的计算器”,本身不存原始数据,只负责根据依赖生成新数据。 - 依赖关系不同:
ref自己就是“数据源”,修改ref的.value会触发视图更新;computed的结果完全依赖其他响应式数据(ref、reactive里的数据),它自己没法主动修改,只能被动跟着依赖变。 - 缓存机制不同:
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或者单独的异步函数处理。- 别把
computed当method用,比如明明只需要简单的拼接字符串,却用computed包一层,反而多了不必要的依赖跟踪,这种场景直接用method或者直接在模板里写表达式更简单。
从原理看 ref 和 computed 为啥不一样?
得从 Vue3 的响应式系统底层逻辑讲:
- ref 的响应式原理:对于基本类型,
ref创建了一个带value属性的对象,通过拦截value的读取和修改来实现响应式(Vue3 里对基本类型用类似Object.defineProperty的逻辑包装,对对象则内部调用reactive),每次读value时收集依赖(比如哪个组件在用这个值),每次改value时触发依赖更新(通知组件重新渲染)。 - computed 的响应式原理:
computed内部有个“dirty”标记,默认是true,第一次访问computed的值时,会执行计算函数,把结果缓存,然后把dirty设为false,之后如果依赖的响应式数据(比如某个ref或reactive里的属性)变化了,Vue 会把dirty设为true,下一次访问computed的值时,先检查dirty:如果是true,就重新执行计算函数,更新缓存,再把dirty设为false;如果是false,直接返回缓存的结果,这样就实现了“依赖变化才重新计算”的缓存逻辑。
简单总结下:ref 是响应式的“载体”,负责让数据能被 Vue 跟踪变化;computed 是响应式的“计算器”,负责基于已有数据做高效计算,开发时先想清楚需求:存数据用 ref,算数据用 computed,避开 .value 和异步这些坑,代码既高效又不容易出 bug~
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
code前端网



