VUE3基础之标签的ref属性

867 阅读3分钟

还记得前面讲的ref创建响应式数据吧,现在这里又出现一个ref,只不过是标签里的ref,这两者可不是同一个东西,现在就让我们看看这个标签里的ref是做什么的吧。

标签里的ref属性

我们都是怎么获取dom结构的呢?,用原生js的可能会说通过dom操作去获取dom元素,学过vue2的可能会说用this.$refs.xxx去获取dom结构,这就需要在dom结构上先设置一个ref属性,用于标识这个dom结构,我们这里vue3的标签中的ref属性和vue2的也差不多。

用原生js的方法获取dom元素

我们先用原生jsdom操作的方法去获取。

<template>
  <h1>中国</h1>
  <h2 id="target">北京</h2>
  <h3>上海</h3>
  <button @click="showLog">打印h2的元素</button>
</template>

<script setup lang="ts">
function showLog() {
  console.log(document.getElementById("target"));
}
</script>

可以看到也是成功的将目标dom元素打印出来了。

1746706638771.png

但是这个方法有一个问题,就是如果我在这个组件里嵌套另外一个组件的话,这两个组件里都有这个id=target,这个时候我们还能获取到想要的dom元素吗?

1746706942660.png

1746706987657.png

可以看到打印出来的不是我们想要的dom元素,大家都知道,无论组件怎么嵌套,最后都会在一个页面里面展示,又因为在同一个页面里面是不可能出现两个id相同的,根据页面的渲染顺序,那个迷惑性的dom元素是要比我们想获取的dom元素先渲染的,所以我们获取到的是那个迷惑性的dom元素,这里可不存在什么后来的会覆盖前面的。

这里我们就看出来了,在多人开发的时候,如果通过id去获取dom元素是比较容易出现冲突的。这也是为什么vue2里会更推荐用this.$refs.xxx去获取dom元素。

vue3通过标签的ref属性去获取dom元素

那么vue3是怎么获取dom元素的呢?其实跟vue2也差不多,都是通过给dom元素添加ref属性去获取,但是vue3没有this,所以使用起来有一些不一样。

vue3会先通过ref函数声明一个空的变量,变量名必须和上面想要获取的dom元素的ref属性值相同,然后这个变量会作为一个容器,自动存储上面ref属性标记的dom元素。这里有点绕,第一个ref是声明响应式数据的函数ref,第二个和第三个ref是标签的属性。

1746708320775.png

没有人问打印的时候为什么要加.value吧,因为这是一个ref函数声明的响应式变量。

不同组件的ref属性标记的dom元素是不会产生冲突的,哪怕是在一个页面也不会产生冲突,所以此时哪怕同个页面的其他组件也有相同的ref属性标记的dom元素,我们依旧可以获取到我们想要的dom元素。

1746708220845.png

我们也可以在其他组件也设置一个按钮去获取dom元素去证明一下。

1746708404970.png

1746708421197.png

vue3在组件上使用ref属性

前面讲了在标签上使用ref属性可以获取到这个dom元素,那么在组件上使用ref属性能够获取到什么呢?

我们在父组件里面对嵌套的子组件添加ref属性去试试看。

1746709003026.png

1746709041247.png

可以看到确实是打印出来一个实例对象,而且是Proxy对象,这是我们在讲reactive创建响应式数据的时候出现过的,但是那时候我们可以通过Proxy对象的target属性去看到这个对象的属性,可是在这里我们怎么看不到子组件的内容呢。

其实也很正常,因为子组件没有允许父组件去看,这也是一种保护机制,那么父组件怎么才能看到子组件里面的东西呢?这就需要牵扯到一个东西叫做defineExpose,这东西自然也是一个函数,在子组件引入这个东西以后,往defineExpose函数传入一个参数对象,这个对象里面的东西就是可以给父组件看的东西。

1746709415157.png

1746709440707.png

而且此时不仅可以看到,还可以对其进行操作。

1746709774797.png

1746709678281.png

然后我们去进行操作,通过点击按钮去修改子组件的变量a,看看是否可以生效。

1746709799871.png

发现也是可以生效的,而却也不会破坏它的响应式,这就说明defineExpose可以把子组件的内容过暴露给父组件并供其使用。

会不会有人问,变量a不是一个RefImpl对象吗,为什么打印的时候就只是一个普通的字符串啊,而且操作的时候不应加上.value吗,其实是把target看作是一个ref函数创建的复杂类型的响应式数据就好了,根据我们那时候讲的知识点,target.value就相当于reactive({a:0, b: 0, c: 0, d: 0}),是一个Proxy对象,我们是可以直接通过target.value.a去操作的。

贴一个那个帖子的截图:

1746710527812.png

至于为什么打印变量a的时候只是一个普通的字符串,应该也是Proxy对象的特性吧。我做了测试,发现确实如此。

let a = ref(1);
let obj = reactive({ a });
function showLog() {
  console.log(obj);
  console.log(obj.a);
}

1746710754035.png