Vue2和Vue3如何玩转DOM

216 阅读1分钟

Vue2的方法

Vue2可以使用ref属性和this.$refs去获取DOM,但是不同情况下会有些许区别。

  1. ref 在 html 标签上
<div ref="myDom"></div>

此时可以直接取到DOM对象:

this.$refs.myDom  // 拿到DOM对象
  1. ref 在 vue 组件上
 <HelloWorld ref="myComp" msg="Welcome to Your Vue.js App"/>

此时拿到的是vue组件对象,需要再访问$el属性才能拿到DOM对象:

this.$refs.myComp // 拿到组件对象
this.$refs.myComp.$el // 拿到DOM对象
  1. ref 在 v-for 中使用
<div v-for="(item,index) in list" :key="item" :ref="'myArr' + index"></div>

在v-for中使用的ref,取到的都是一个数组,需要访问第一个元素才能拿到DOM对象;

this.$refs.myArr0 // 这是数组
this.$refs.myArr0[0] // 拿到第一个div的DOM对象

截屏2024-02-23 23.19.34.png

Vue3的方法

Vue3也是使用ref去定义DOM,但是Vue3中,组件中的this并不是指向组件对象,因此不能使用this.$refs去访问DOM,但Vue3中,提供了更简洁的方式去获取DOM

  1. 上述的例子,在Vue3中是这样的访问和输出:
const myDom = ref(null)
const myComp = ref(null)
const myArr0 = ref(null)

console.log(unref(myDom)) // 拿到DOM对象
console.log(unref(myComp)) // 组件的proxy
console.log(unref(myComp).$el) // 组件的DOM对象
console.log(unref(myArr0)) // 数组的代理
console.log(unref(myArr0)[0]) // DOM对象

截屏2024-02-23 23.36.24.png

  1. 我们发现,当在v-for中使用ref的时候,在Vue3中需要定义很多个变量,非常不方便。ref属性实际上也可以接收一个函数,函数的参数就是我们需要的DOM对象。
<div :ref="getMyDom"></div>
<div v-for="(item,index) in list" :key="item" :ref="getMyArr">{{index}}</div>
function getMyDom(el) {
    console.log(el) // 拿到DOM对象
}

function getMyArr(el) {
  console.log(el) // 按顺序输出了div对象
}

Vue3的ref封装

我们可以定义一个hooks,将获取DOM的方法封装起来:

useRefs.ts:代码来自于vben-admin

import type { Ref } from 'vue';
import { ref, onBeforeUpdate } from 'vue';

export function useRefs(): [Ref<HTMLElement[]>, (index: number) => (el: HTMLElement) => void] {
  const refs = ref([]) as Ref<HTMLElement[]>;

  onBeforeUpdate(() => {
    refs.value = [];
  });

  const setRefs = (index: number) => (el: HTMLElement) => {
    refs.value[index] = el;
  };

  return [refs, setRefs];
}

注意这里的setRefs,返回值是一个函数,因此我们在template中使用的时候,是:ref="setRefs(index)",这样就能把DOM传给setRefs(index)函数了,然后这个DOM会存到refs变量里。

使用方法:

<ul>
  <li
    :ref="setRefs(index)"
    v-for="(item, index) in searchResult"
  </li>
</ul>
import { useRefs } from '/@/hooks/core/useRefs';
const [refs, setRefs] = useRefs();
console.log(refs.value) // refs是一个数组,按顺序把li的DOM放到这个数组里了。