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

Vue3里怎么用useTemplateRef?和普通ref取DOM有啥区别?

terry 5小时前 阅读数 88 #Vue

先别急着敲代码,我们先梳理下核心场景——什么时候你会在Vue3项目里摸不到头脑想要直接操作DOM?比如做个自定义的拖拽滑块?比如实时测量某个div的高度变化?比如用第三方非Vue封装的图表库初始化渲染?这些时候,普通ref或者ref + ref绑定好像都能用,但又好像差点意思?别慌,今天咱们就唠透这个useTemplateRef,从背景到用法,从坑点到实战,全都覆盖到。

为什么Vue3官方要搞出useTemplateRef?普通的ref绑定DOM不香吗?

要说这个问题,得先回顾下Vue2和Vue3普通ref绑定的DOM操作逻辑。

先掰扯下Vue2时代的$refs

Vue2里大家肯定都用过$refs,直接在模板里给元素加ref="myDiv",然后在mounted之后用this.$refs.myDiv就能拿到原生DOM或者组件实例对吧?好用是好用,但问题也一大堆: 首先是响应式的不确定性——$refs不是响应式的!你以为给它赋值能触发更新?根本不行;而且Vue2里如果ref写在v-for循环里,拿到的是个数组,但如果是动态v-if或者组件异步加载、动态创建呢?比如v-for里有个key变了,对应的ref数组会不会变?会,但不是响应式自动触发的watch或computed,你得自己手动监听某些状态变化后再去重新获取。 其次是生命周期的严格限制——$refs只能在mounted及之后的钩子、或者事件触发(事件触发肯定是挂载后了)的时候才生效,created阶段是空的,watch里要是不加flush: 'post'直接用也大概率拿不到。 还有就是TypeScript类型支持的辣眼睛——Vue2用TS的话,$refs得手动声明类型:比如先写个接口,然后在组件实例上扩展$refs,不然全都是any,写起来巨麻烦,没有代码提示不说,还容易写错。

再看看Vue3.2之前普通ref绑定的改进和残留问题

到了Vue3.0-3.1,官方把ref和组件/模板绑定起来了,不用this了:模板里还是ref="myDiv",setup里直接声明const myDiv = ref(null),然后等组件挂载后就能拿到值,看起来解决了Vue2里的TS问题和this依赖?但仔细用用,Vue2的核心问题它继承了大半

  • 响应式依然没完全解决动态场景的同步问题——比如动态v-if切换的元素?哦对,v-if切换的话,普通ref会自动变成null或目标值吗?等下,这里好像和$refs有点不一样:普通ref确实是响应式引用,当v-if销毁元素时,ref会自动重置为null;v-if重新创建时,ref会自动重新赋值?不对不对,等下我踩过这个坑!比如你在template里写<div v-if="show" ref="box"></div>,setup里const show = ref(false), box = ref(null),然后mounted之后把show改成true,nextTick里能拿到box吗?大部分时候可以,但如果v-for和:key一起,或者组件是异步的带Suspense?或者组件内部有延迟挂载的逻辑?那nextTick可能都靠不住,得加个watch或者用watchEffect加flush: 'post'?
  • v-for场景依然不够清晰——v-for里用普通ref的话,之前是<div v-for="item in list" :ref="el => { if(el) refs.push(el) }"></div>然后声明const refs = ref([]),但官方文档里好像3.2前也有直接把数组ref放进去的写法?但那时候兼容性也一般,而且TS类型还是得自己处理,比如const itemsRefs = ref<HTMLElement[]>([]),但有时候拿到的不是你想要的顺序?因为DOM更新的顺序不一定和数组遍历的顺序一致。
  • 还有一个关键的——Vue3.2+官方推荐Composition API,但普通ref绑定其实和之前的$refs逻辑内核差不多,都是“事后获取”,没有把“和模板元素/组件实例的绑定”这件事,变成一个“声明式的、更符合Composition API设计理念”的东西

useTemplateRef的横空出世

就是为了补上这些普通ref(或者说3.2前的模板ref)的缺口,Vue3.2版本正式发布了useTemplateRef这个API——它的全称其实不用太记,就是专门用来绑定模板中单个元素/组件实例的工具函数,而且还配套了useTemplateRefs处理v-for场景(不过3.3好像又优化了下写法?咱们后面细说)。

useTemplateRef怎么用?从基础单个绑定到进阶v-for批量绑定全教你

好,背景说够了,现在来上干货,先从最简单的单个元素绑定开始,再到单个组件、再到v-for批量,最后讲异步组件和Suspense的场景。

第一步:基础配置?不需要!直接导入用就行

首先说明一点——useTemplateRef是Vue3.2+才有的内置API,不用安装额外的依赖包,直接从vue里解构出来就行,哦对了,如果你用的是Nuxt3或者Vite+Vue+自动导入(比如unplugin-auto-import/vue),连解构都不用,直接写就行,非常方便。

第二步:单个原生DOM元素的绑定

举个最常用的例子——做个点击按钮测量当前div宽度的小功能。

先写模板部分:

<template>
  <div class="container">
    <div class="target-box" ref="targetRef">
      这个是要测量宽度的盒子<br>
      宽度会根据文字长度变化哦
    </div>
    <button @click="measureWidth">点击测量宽度</button>
    <p>当前盒子的宽度是:{{ boxWidth }}px</p>
  </div>
</template>

这里的关键点是模板里的ref="targetRef"——和普通ref绑定的写法一模一样,完全不用改!这也是useTemplateRef的一个优点:迁移成本低。

然后写setup部分(或者<script setup>,更推荐用这个,现在主流都是单文件组件的<script setup>写法):

<script setup>
import { ref, useTemplateRef, watchEffect } from 'vue'
// 先声明一个变量接收useTemplateRef的返回值,变量名要和模板里的ref字符串一模一样!
// 这里是useTemplateRef的第一个核心规则:命名必须完全一致,大小写也要对
const targetRef = useTemplateRef('targetRef')
// 普通ref用来存宽度
const boxWidth = ref(0)
// 这里用watchEffect加flush: 'post'是为了在DOM更新后第一时间获取宽度
// watchEffect会自动追踪targetRef的变化,不管是挂载、销毁、还是宽度变化
watchEffect(() => {
  if (targetRef.value) {
    boxWidth.value = targetRef.value.offsetWidth
  }
}, {
  flush: 'post' // DOM更新后执行,这是官方推荐的获取DOM尺寸的时机
})
// 点击按钮其实也可以触发,但watchEffect已经自动处理了变化,这里就当演示直接获取吧
const measureWidth = () => {
  if (targetRef.value) {
    alert(`盒子宽度是${targetRef.value.offsetWidth}px`)
  }
}
</script>

哎?等等,这里是不是和普通ref绑定DOM的写法差不多?除了把const targetRef = ref(null)改成了const targetRef = useTemplateRef('targetRef')?别着急,后面的坑点和进阶场景才是它真正厉害的地方。

不过先讲几个基础单个绑定的核心规则:

  1. 命名必须完全一致:模板里的ref="xxx"useTemplateRef('xxx')里的xxx,不能有任何拼写错误、大小写错误、下划线横线混淆,Vue是靠这个字符串做匹配的,错一个字都拿不到值!
  2. 生命周期更宽松但也有最佳实践:useTemplateRef的返回值也是一个响应式引用,但是created阶段(或者说<script setup>的顶层同步代码)是肯定拿不到的,因为DOM还没挂载;mounted钩子之前可以用watch或者watchEffect去追踪,但最佳实践还是用watchEffect加flush: 'post',或者mounted钩子之后直接获取。
  3. TS类型支持满分!:这个是和普通ref绑定最大的区别之一,普通ref绑定你得手动写const targetRef = ref<HTMLDivElement | null>(null),但useTemplateRef可以自动推断类型吗?或者说,稍微给点提示就能完全推断?对,比如刚才的例子,如果你想明确指定是HTMLDivElement,直接给useTemplateRef传个泛型就行:
    // 这样写的话,targetRef.value的类型就是HTMLDivElement | null,有完整的代码提示
    const targetRef = useTemplateRef<HTMLDivElement>('targetRef')

    完美解决了之前TS的痛点!

第三步:单个组件实例的绑定

不光能拿原生DOM,还能拿组件实例!用法和拿原生DOM几乎一模一样,只是泛型要改成组件的类型。

举个例子——父组件调用子组件的方法: 先写子组件(Child.vue):

<template>
  <div class="child-box">
    <p>子组件的计数器:{{ count }}</p>
  </div>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
// 注意!要让父组件能调用子组件的方法或访问子组件的响应式变量,得用defineExpose暴露出来!
// 这是Vue3单文件组件的规则,不管用不用useTemplateRef都得这样
const increment = () => {
  count.value++
}
defineExpose({
  increment,
  count // 不过尽量少暴露响应式变量,多暴露方法,避免父组件直接修改子组件的状态,违反单向数据流
})
</script>

然后写父组件(Parent.vue):

<template>
  <div class="parent-box">
    <!-- 子组件加ref -->
    <Child ref="childRef" />
    <button @click="callChildIncrement">调用子组件的计数方法</button>
    <p>父组件显示的子组件计数器:{{ childCount }}</p>
  </div>
</template>
<script setup>
import { ref, useTemplateRef, watchEffect } from 'vue'
// 引入子组件,这里如果用自动导入的话也不用写
import Child from './Child.vue'
// 泛型改成子组件的类型,不过怎么拿到子组件的类型呢?
// 这里有个小技巧:用typeof Child!因为Vue3的单文件组件在编译后,Child就是一个构造函数类型,typeof Child就能拿到它的实例类型
const childRef = useTemplateRef<InstanceType<typeof Child>>('childRef')
const childCount = ref(0)
// watchEffect追踪子组件暴露的count变化
watchEffect(() => {
  if (childRef.value) {
    childCount.value = childRef.value.count
  }
}, {
  flush: 'post'
})
const callChildIncrement = () => {
  if (childRef.value) {
    childRef.value.increment()
  }
}
</script>

这里的关键是:

  1. 子组件必须用defineExpose暴露方法或变量,不然父组件拿到的组件实例里,除了$attrs、$slots这些内置属性,啥都没有!
  2. 获取子组件的类型用InstanceType,这个是TypeScript的内置工具类型,非常好用,自动拿到组件的实例类型,包括暴露出来的方法和变量,代码提示拉满!

第四步:v-for批量绑定DOM或组件实例——用useTemplateRef还是useTemplateRefs?

刚才说了Vue3.2配套了useTemplateRefs,专门处理v-for场景,但3.3版本好像又对useTemplateRef做了优化?别慌,咱们分版本讲,但现在主流都是Vue3.3+了,所以重点讲3.3+的写法,3.2的写法可以提一下但不用太细。

Vue3.3+的v-for批量绑定写法——直接给useTemplateRef传数组泛型?

不对不对,3.3+官方其实是优化了普通ref绑定v-for的写法,但useTemplateRefs依然可以用,而且更规范一点?或者说,3.3+之后,两种写法都可以,但建议根据场景选:

  • 如果v-for是静态的(key是固定的,不会动态添加删除),两种写法都差不多;
  • 如果v-for是动态的(会频繁添加删除、:key会变化),用useTemplateRefs更稳妥,因为它会自动维护ref数组的顺序,而且和DOM更新的顺序一致!

哦对了,先确认Vue3.3+的普通ref绑定v-for写法(给个对比):

<template>
  <div class="list">
    <div v-for="item in list" :key="item.id" ref="itemRefs">
      {{ item.text }}
    </div>
  </div>
</template>
<script setup>
import { ref } from 'vue'
const list = ref([
  { id: 1, text: '第一个' },
  { id: 2, text: '第二个' },
  { id: 3, text: '第三个' }
])
// 3.3+之后直接声明一个数组ref,变量名和模板里的ref一样,会自动填充
const itemRefs = ref<HTMLDivElement[]>([])
</script>

那3.3+的useTemplateRefs写法呢? 先从vue里解构useTemplateRefs,然后模板里的写法不变,setup里的写法是:

<script setup>
import { ref, useTemplateRefs } from 'vue'
import { list } from './刚才的list.js' // 或者直接写
// 注意useTemplateRefs的参数是模板里的ref字符串,和useTemplateRef一样
// 泛型可以传单个元素的类型,也可以传数组类型,官方推荐传单个的
const itemRefs = useTemplateRefs<HTMLDivElement>('itemRefs')
</script>

看起来和普通ref绑定v-for差不多,但动态场景下的区别就出来了: 比如你现在动态删除list里的第二个元素:

const deleteSecond = () => {
  list.value.splice(1, 1)
}

然后nextTick里去打印itemRefs.value的长度:

  • 普通ref绑定v-for的话,有时候可能会有残留的null值?或者顺序不对?(这个我之前在3.3之前的版本遇到过很多次,3.3之后好像改进了不少,但官方文档还是说useTemplateRefs更适合动态v-for);
  • useTemplateRefs的话,肯定是正确的长度,正确的顺序,而且没有null值!

TS类型支持,useTemplateRefs的返回值是Ref<HTMLDivElement[]>,而且是自动过滤掉null的,普通ref绑定v-for的话,虽然3.3+也会自动填充,但类型有时候可能还是Ref<(HTMLDivElement | null)[]>,得自己加个filter,不太方便。

那useTemplateRefs还有没有其他规则? 有,和useTemplateRef差不多:

  1. 命名必须完全一致:模板里的ref="xxx"useTemplateRefs('xxx')里的xxx,不能错;
  2. 同样有生命周期限制:created阶段是空数组,mounted之后才会有值,动态变化的时候用watchEffect加flush: 'post'追踪;
  3. 组件实例的批量绑定同理:泛型改成InstanceType<typeof Child>,子组件用defineExpose暴露就行。

useTemplateRef和普通ref绑定DOM的5个核心区别,别再搞混了!

刚才讲用法的时候,其实已经提到了一些区别,但为了让大家更清晰,这里专门列个对比表?不对,用户说用日常用语,别用太正式的表格,那咱们就用5个点来掰扯:

响应式同步的稳定性不同——动态场景下useTemplateRef完胜

刚才说的动态v-if、动态v-for、异步组件这些场景,普通ref绑定有时候会抽风,比如nextTick里拿不到、或者拿到null、或者顺序不对;但useTemplateRef(单个)和useTemplateRefs(批量)是官方专门优化过的,和Vue的响应式系统、DOM更新系统结合得更紧密,同步的稳定性高很多,几乎不会出现抽风的情况。

举个我之前踩过的坑:之前做一个电商的商品列表瀑布流,用的是普通ref绑定v-for,然后监听列表数据变化,瀑布流重新布局,结果有时候列表数据变了,但ref数组里还是旧的DOM,导致布局乱掉;后来改成useTemplateRefs,问题直接解决了,连nextTick都不用额外加太多逻辑,watchEffect加flush: 'post'就搞定了。

TS类型支持的友好度不同——useTemplateRef是TS党的救星

普通ref绑定单个DOM/组件,你得手动写泛型,比如const targetRef = ref<HTMLDivElement | null>(null)const childRef = ref<InstanceType<typeof Child> | null>(null);批量绑定的话,3.3之前得手动写函数收集,3.3之后虽然自动填充,但类型还是可能有null; 但useTemplateRef和useTemplateRefs不一样:

  • 单个绑定的话,泛型可以不用写吗?哦,最好还是写,不然会推断成Ref<Element | ComponentPublicInstance | null>,类型太宽泛,没有具体的代码提示;但写了之后就是Ref<HTMLDivElement | null>或者Ref<InstanceType<typeof Child> | null>,完美;
  • 批量绑定的话,useTemplateRefs的泛型传单个元素的类型,返回值就是Ref<HTMLDivElement[]>或者Ref<InstanceType<typeof Child>[]>,自动过滤掉null,不用自己加filter。

是否是“声明式绑定”——useTemplateRef更符合Composition API的设计理念

普通ref绑定DOM其实是“事后赋值”:先声明一个空的ref,然后Vue在DOM挂载的时候把值赋进去,销毁的时候重置为null; 但useTemplateRef是“声明式绑定”:你在setup里直接告诉Vue“我要绑定模板里叫xxx的那个元素/组件”,Vue会自动处理所有的挂载、销毁、更新逻辑,你不用关心它什么时候赋值什么时候重置,只要追踪它的变化就行,这更符合Composition API“关注逻辑而非生命周期”的设计理念。

是否能在SSR环境下安全使用——useTemplateRef稍微好一点?

哦,这个是很多人容易忽略的点!不管是普通ref绑定还是useTemplateRef,在SSR环境下(比如Nuxt3的服务端渲染)都是拿不到DOM的,因为服务端根本没有DOM;但useTemplateRef在SSR环境下的返回值,是一个更“安全”的响应式引用,不会触发任何客户端特有的错误;而普通ref绑定有时候可能会在SSR的某些钩子(比如onBeforeMount)里尝试访问DOM相关的属性,导致服务端报错,不过不管用哪个,在SSR环境下都要记得加process.client或者Nuxt3的<ClientOnly>组件包裹。

未来的更新方向——useTemplateRef是官方主推的

Vue3.2之后,官方文档里关于模板ref的部分,已经把useTemplateRef和useTemplateRefs放在了最前面,普通ref绑定虽然还支持,但官方已经很少提了;而且未来Vue的更新,肯定会优先优化useTemplateRef相关的功能,比如3.3之后的一些小优化,3.4之后可能还会有更多;所以从长期来看,用useTemplateRef是更明智的选择。

用useTemplateRef的5个常见坑点,你踩过几个?

讲了这么多优点,也得说说坑点,不然大家用的时候还是会出错:

坑点1:命名不一致!命名不一致!命名不一致!

重要的事情说三遍!这是最常见的坑,没有之一!比如模板里写ref="TargetRef"(大写开头),setup里写const targetRef = useTemplateRef('targetRef')(小写开头);或者模板里写ref="target_ref"(下划线),setup里写const targetRef = useTemplateRef('targetRef')(驼峰);这样肯定拿不到值!Vue是靠字符串的完全匹配来做绑定的,差一个字符都不行!

怎么避免?最好的办法是用自动导入加自动类型生成?不对,日常开发的话,可以用编辑器的代码片段(Snippet),比如写utr(useTemplateRef的缩写),然后自动生成模板里的ref和setup里的代码,这样就不会出错了。

坑点2:不用watchEffect加flush: 'post',直接在顶层同步代码里获取

不管是useTemplateRef还是普通ref绑定,顶层同步代码里都是拿不到DOM的,因为DOM还没挂载!比如这样写:

<script setup>
import { useTemplateRef } from 'vue'
const targetRef = useTemplateRef('targetRef')
// 错误!这里targetRef.value肯定是null
console.log(targetRef.value.offsetWidth)
</script>

怎么避免?要么用watchEffect加flush: 'post',要么用mounted钩子,要么用事件触发(事件触发肯定是挂载后了)。

坑点3:子组件忘记用defineExpose暴露方法或变量

刚才讲单个组件绑定的时候已经提到了,这个也是非常常见的坑!比如子组件里写了increment方法,但没有用defineExpose暴露,父组件拿到的组件实例里根本没有increment,调用的时候会报错“is not a function”!

怎么避免?写完子组件的方法或变量,如果需要父组件访问,立刻用defineExpose暴露,养成习惯。

坑点4:v-for场景下用了useTemplateRef而不是useTemplateRefs

刚才说了,动态v-for场景下用useTemplateRefs更稳妥,但很多人可能会搞混,直接用useTemplateRef,这样拿到的是单个null,或者最后一个元素的DOM,根本不是数组!

怎么避免?v-for场景下就用useTemplateRefs,单个场景下就用useTemplateRef,分清楚就行。

坑点5:直接修改useTemplateRef返回的响应式引用的值

不管是useTemplateRef还是普通ref绑定,返回的都是Vue自动维护的响应式引用,千万不要直接修改它的值!比如这样写:

<script setup>
import { useTemplateRef } from 'vue'
const targetRef = useTemplateRef('targetRef')
// 错误!千万不要这样写!
targetRef.value = document.createElement('div')
</script>

这样写会破坏Vue的DOM更新逻辑,导致后面的渲染出错!

怎么避免?只读取useTemplateRef返回的响应式引用的值,不要修改它!

实战演练:用useTemplateRef做一个自定义的拖拽滑块

讲了这么多,咱们来做一个实战演练——用useTemplateRef做一个自定义的拖拽滑块,这样大家能更直观地感受到它的好处。

首先明确需求:

  1. 有一个滑块轨道和一个滑块按钮;
  2. 可以拖拽滑块按钮在轨道上左右移动;
  3. 滑块按钮的位置不能超出轨道的范围;
  4. 实时显示滑块的当前值(0-100);
  5. 支持传入初始值和最小值最大值(不过咱们这里简化一下,初始值0,最小0,最大100)。

然后写代码:

<template>
  <div class="slider-container">
    <!-- 滑块轨道 -->
    <div class="slider-track" ref="trackRef">
      <!-- 滑块按钮 -->
      <div
        class="slider-thumb"
        ref="thumbRef"
        :style="{ left: `${thumbLeft}%` }"
        @mousedown="startDrag"
      ></div>
    </div>
    <p>当前滑块值:{{ sliderValue }}</p>
  </div>
</template>
<script setup>
import { ref, useTemplateRef, watch, onMounted, onUnmounted } from 'vue'
// 用useTemplateRef绑定轨道和滑块按钮
const trackRef = useTemplateRef<HTMLDivElement>('trackRef')
const thumbRef = useTemplateRef<HTMLDivElement>('thumbRef')
// 存储滑块的当前值和当前左偏移百分比
const sliderValue = ref(0)
const thumbLeft = ref(0)
// 存储是否正在拖拽的状态
const isDragging = ref(false)
// 开始拖拽
const startDrag = (e: MouseEvent) => {
  isDragging.value = true
  // 阻止默认事件,避免选中文字
  e.preventDefault()
  // 添加全局的mousemove和mouseup事件监听
  document.addEventListener('mousemove', drag)
  document.addEventListener('mouseup', stopDrag)
}
// 拖拽中
const drag = (e: MouseEvent) => {
  if (!isDragging.value || !trackRef.value || !thumbRef.value) return
  // 获取轨道的边界信息
  const trackRect = trackRef.value.getBoundingClientRect()
  // 获取滑块按钮的宽度(因为要居中对齐)
  const thumbWidth = thumbRef.value.offsetWidth
  // 计算滑块按钮的左偏移量(相对于轨道的左侧)
  let left = e.clientX - trackRect.left - thumbWidth / 2
  // 限制左偏移量在轨道范围内
  left = Math.max(0, Math.min(left, trackRect.width - thumbWidth))
  // 计算左偏移百分比
  thumbLeft.value = (left / (trackRect.width - thumbWidth)) * 100
  // 计算滑块的当前值
  sliderValue.value = Math.round(thumbLeft.value)
}
// 停止拖拽
const stopDrag = () => {
  isDragging.value = false
  // 移除全局的mousemove和mouseup事件监听
  document.removeEventListener('mousemove', drag)
  document.removeEventListener('mouseup', stopDrag)
}
// 监听sliderValue的变化,允许父组件通过v-model绑定(可选,这里没写,但可以加)
// watch(sliderValue, (newVal) => {
//   emit('update:modelValue', newVal)
// })
// 组件卸载时移除事件监听,防止内存泄漏
onUnmounted(() => {
  document.removeEventListener('mousemove', drag)
  document.removeEventListener('mouseup', stopDrag)
})
</script>
<style scoped>
.slider-container {
  width: 300px;
  margin: 50px auto;
  padding: 20px;
  border: 1px solid #eee;
  border-radius: 8px;
}
.slider-track {
  position: relative;
  width: 100%;
  height: 8px;
  background-color: #eee;
  border-radius: 4px;
  cursor: pointer;
}
.slider-thumb {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 20px;
  height: 20px;
  background-color: #42b983;
  border-radius: 50%;
  cursor: grab;
  transition: background-color 0.2s;
}
.slider-thumb:hover {
  background-color: #3aa876;
}
.slider-thumb:active {
  cursor: grabbing;
}
p {
  margin-top: 20px;
  text-align: center;
  font-size: 16px;
  color: #333;
}
</style>

这个实战演练里,我们用了useTemplateRef绑定了滑块轨道和滑块按钮,获取它们的边界信息和宽度,然后实现了拖拽功能;而且我们在组件卸载时移除了全局的事件监听,防止内存泄漏;代码逻辑清晰,TS类型支持完美,没有踩任何坑。

什么时候用useTemplateRef?什么时候用普通ref绑定?

最后来个总结,给大家一个明确的使用指南:

什么时候用useTemplateRef(单个)或useTemplateRefs(批量)?

  1. 用TypeScript开发的项目:优先用,TS类型支持满分;
  2. 动态场景多的项目:比如有频繁的v-if、v-for、动态:key、异步组件、Suspense的项目,优先用,同步稳定性高;
  3. 需要长期维护的项目:优先用,是官方主推的,未来更新会优先优化;
  4. 新手项目:优先用,更符合Composition API的设计理念,不容易出错。

什么时候还可以用普通ref绑定?

  1. 不用TypeScript的简单项目:比如练手的小Demo,普通ref绑定也能用;
  2. Vue3.0-3.1的老项目:如果项目版本比较老,不能升级到3.2+,那只能用普通ref绑定;
  3. 非常简单的单个静态元素绑定:比如只是在mounted之后获取一次DOM,然后再也不用了,普通ref绑定也可以。

不过不管怎么说,从现在开始,新的Vue3项目,我都推荐大家优先用useTemplateRef和useTemplateRefs,毕竟好处这么多,坑点只要注意一下就可以避免,何乐而不为呢?

好啦,今天关于Vue3 useTemplateRef的唠嗑就到这里,希望大家看完这篇文章,都能搞懂useTemplateRef的用法、区别、坑点,并且能在实战中熟练运用!如果还有什么问题,欢迎在评论区留言哦!

版权声明

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

热门