Vue3 useTemplateRef获取模板引用的组合式API
Vue 3.5 版本引入了 useTemplateRef API,它提供了一种更符合直觉且类型安全的方式来获取 DOM 元素或组件实例。相比旧版的 ref(null) 写法,新 API 减少了冗余代码,并自动推导模板引用的类型。以下是基于 Vue 3.5+ 的详细操作指南。
1. 环境准备与基础用法
在使用此 API 之前,必须确保项目依赖的 Vue 版本不低于 3.5。
- 打开终端,运行以下命令检查当前 Vue 版本。
npm list vue - 若版本低于 3.5,执行升级命令。
npm install vue@latest
useTemplateRef 的核心逻辑在于“字符串匹配”。它在模板中寻找与传入参数同名的 ref 属性,并自动建立连接。
-
创建一个新的 Vue 组件文件
TemplateRefDemo.vue。 -
编写模板代码,在目标元素上添加
ref属性并赋予一个唯一的字符串名称(例如"inputRef")。<script setup lang="ts"> import { useTemplateRef } from 'vue' // 步骤 5:调用 API 并传入模板中定义的字符串名称 const inputRef = useTemplateRef<HTMLInputElement>('inputRef') // 步骤 6:定义聚焦方法 const focusInput = () => { // 注意:通过 .value 访问 DOM 元素 if (inputRef.value) { inputRef.value.focus() } } </script> <template> <div> <!-- 这里的 ref 字符串 "inputRef" 必须与上方 useTemplateRef 的参数一致 --> <input ref="inputRef" type="text" placeholder=" focus me" /> <button @click="focusInput">聚焦输入框</button> </div> </template>
2. 对比旧方案与新方案
为了理解 useTemplateRef 的优势,我们需要将其与 Vue 3.5 之前的通用写法进行对比。下表展示了两种方案在实现相同功能时的差异。
| 特性 | 旧方案 (Generic Ref) | 新方案 (useTemplateRef) |
|---|---|---|
| 变量定义 | 需显式声明 const inputRef = ref(null) |
直接调用 const inputRef = useTemplateRef('input') |
| 类型泛型 | 需手动指定 ref<HTMLInputElement \| null>(null) |
自动根据模板上下文推导,或简写为 useTemplateRef<HTMLInputElement> |
| 匹配逻辑 | 变量名必须与模板 ref 名完全一致 | 函数参数字符串必须与模板 ref 名一致 |
| 初始化值 | 必须传入 null |
无需传入初始值 |
3. 处理组件引用
除了获取原生 DOM 元素,useTemplateRef 同样适用于获取子组件实例,并调用其暴露的方法。
-
创建子组件
ChildComponent.vue,并使用defineExpose暴露内部方法。<script setup lang="ts"> const childMethod = () => { console.log('子组件方法被调用') } // 暴露方法给父组件 defineExpose({ childMethod }) </script> <template> <div>我是子组件</div> </template> -
回到父组件,引入
ChildComponent。 -
使用
useTemplateRef获取组件实例。<script setup lang="ts"> import { useTemplateRef } from 'vue' import ChildComponent from './ChildComponent.vue' // 获取组件实例,类型为 InstanceType<typeof ChildComponent> const childRef = useTemplateRef<InstanceType<typeof ChildComponent>>('childComp') const callChild = () => { // 安全调用:检查实例是否存在 childRef.value?.childMethod() } </script> <template> <ChildComponent ref="childComp" /> <button @click="callChild">调用子组件方法</button> </template>
4. 利用 TypeScript 增强类型安全
useTemplateRef 支持泛型参数,这为复杂的 DOM 结构或组件实例提供了精确的类型提示。
-
打开组件的
<script>标签。 -
在调用
useTemplateRef时,传入具体的类型作为泛型参数T。例如,获取一个
VideoElement:import { useTemplateRef } from 'vue' // 显式指定泛型为 HTMLVideoElement const videoRef = useTemplateRef<HTMLVideoElement>('videoPlayer') const playVideo = () => { if (videoRef.value) { // 此时 videoRef.value 会自动补全 video 元素的属性和方法 videoRef.value.play() } }如果不传泛型,TypeScript 也会尝试根据
ref在模板中绑定的元素标签进行自动推导,但在组件引用或复杂场景下,显式声明泛型是最稳妥的做法。
5. 生命周期与访问时机
虽然 useTemplateRef 简化了引用获取,但 DOM 元素的挂载时机依然遵循 Vue 的生命周期规则。在组件挂载完成前,ref 的值始终为 null。
-
避免在
setup()顶层直接访问useTemplateRef返回的value。// ❌ 错误:此时元素尚未挂载,输出 null console.log(inputRef.value) -
使用
onMounted钩子确保 DOM 已渲染。import { onMounted } from 'vue' onMounted(() => { // ✅ 正确:此时元素已存在 console.log(inputRef.value) }) -
使用
watch或watchEffect监听 ref 的变化,适用于需要响应式处理 DOM 变化的场景(例如v-if导致元素销毁与重建)。import { watchEffect } from 'vue' watchEffect(() => { if (inputRef.value) { // 每次 inputRef 变为非空时都会执行 inputRef.value.focus() } })

暂无评论,快来抢沙发吧!