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

Vue3里ref最容易踩的坑和必学的核心技巧都有啥?

terry 20小时前 阅读数 330 #Vue

最近刷前端交流群,总能看到刚转Vue3的小伙伴问ref的问题:一会儿说ref的值用.value取不到了?一会儿又纠结什么时候用ref什么时候用reactive?还有人纳闷为啥循环里的ref模板引用不对?其实这些问题,只要摸透ref的底层逻辑和Vue3官方给的设计边界,都能迎刃而解,今天咱们就用问答的方式,从基础到进阶,把ref彻底讲透,连新手容易忽略的小细节都不放过。

先搞懂ref的底层:为什么它要有.value这个“麻烦”?

很多刚从Vue2转过来的同学,最不适应的就是ref后面跟着的.value——明明Vue2用this.$refs或者ref属性就能直接拿到DOM或组件实例,怎么到了Composition API里,连个简单的数字都得加个后缀?其实这不是Vue团队没事找事,反而恰恰是为了让数据响应式更可控、更符合JavaScript原生的行为逻辑。

你可以把ref想象成一个“透明的小盒子”:盒子的名字就是你定义的ref变量,盒子里面装的才是你真正要操作的值(可以是数字、字符串、布尔值、对象、数组,甚至是函数、DOM元素、组件实例),那为什么要用盒子呢?因为JavaScript里的基本类型(比如number、string、boolean、null、undefined、symbol)是“按值传递”的,而对象类型(比如object、array、function)是“按引用传递”的。

Vue3的响应式系统,底层用的是Proxy来代理对象类型,但Proxy没办法直接代理基本类型啊——比如你定义一个let count = 0,不管你怎么改count,它就是个值,不会触发任何代理逻辑,那怎么办?只能给基本类型套个“壳子”,也就是一个只有一个value属性的普通对象,然后用Proxy去代理这个壳子,这样的话,当你修改count.value的时候,Proxy就能监听到变化,然后去更新视图了。

那对象类型为啥有时候也用ref呢?这其实是个习惯问题,或者说场景问题,官方文档里也说了,ref可以接受任何类型的值——如果传的是对象或数组,Vue3内部会自动把这个对象/数组转换成reactive的响应式对象,然后把它赋值给壳子的value属性,也就是说,ref(obj)其实等价于reactive({ value: obj })吗?不对不对,等一下,我刚才是不是说错了?哦不,应该更严谨一点:ref处理非基本类型的时候,内部调用的是toReactive函数,把传入的原始值变成响应式对象,然后再把这个响应式对象赋值给.value,而如果你直接用reactive(obj),得到的就是一个本身就是响应式的对象,不需要再套壳子。

那这时候你可能会问了:既然ref能处理所有类型,那我以后都用ref不就行了?为啥还要留着reactive?别急,后面咱们讲ref和reactive的对比的时候会详细说,现在先记住这个“小盒子”的比喻,这对理解后面的所有内容都很重要。

Vue3 ref的基础用法:你真的会用吗?

基础用法1:定义响应式的基本类型数据

这应该是ref最常用的场景了,比如你要做一个计数器,代码可以这么写:

<script setup>
import { ref } from 'vue'
// 定义一个响应式的数字count,初始值是0
const count = ref(0)
// 定义一个响应式的字符串message,初始值是'Hello Vue3'
const message = ref('Hello Vue3')
// 定义一个响应式的布尔值isShow,初始值是true
const isShow = ref(true)
// 定义一个点击事件函数,修改count的值
const handleClick = () => {
  // 注意哦,在<script setup>或者普通的Composition API函数里,修改ref的值必须加.value
  count.value++
  message.value = `你点击了${count.value}次`
  if (count.value % 5 === 0) {
    isShow.value = !isShow.value
  }
}
</script>
<template>
  <div>
    <h1 v-if="isShow">{{ message }}</h1>
    <button @click="handleClick">点我加1</button>
  </div>
</template>

哎?这里有个奇怪的地方:在