Vue useTemplateRef 怎样获得模板引用类型
来源:17golang原创
时间:2026-11-07 00:18:47 404浏览 收藏
如果你在 Vue 里写过模板引用,通常会遇到两个问题:useTemplateRef() 拿到的到底是什么类型,以及为什么模板已经写了 ref,IDE 仍然提示可能是 null。结论是:Vue 3.5 及以上,静态 SFC 模板引用可以结合 Vue 的语言工具自动推断;推断覆盖不到的动态组件、非 SFC 或复杂类型,则应该显式传入泛型。
官方地址:https://vuejs.org/guide/essentials/template-refs.html
本文只围绕一个小场景展开:让一个搜索输入框、一个子组件和一个动态组件的模板引用获得可靠类型,并处理它们在挂载前或被 v-if 移除后的空值。
先把模板 ref 名称和 useTemplateRef 对齐
useTemplateRef() 的第一个参数不是随便起的变量说明,而是模板中 ref 属性的字符串值。两边对不上时,Vue 语言工具没有稳定的匹配入口,后续自然也无法根据目标元素推断类型。
这里有两个名字:脚本里的变量叫 searchBox,模板里的引用键也叫 searchBox。变量名可以不同,但字符串键必须和模板 ref 值相同。真正参与匹配的是后者。
Vue 3.5 的静态模板引用为什么能自动推断
当模板引用是静态写法时,Vue 3.5 配合 @vue/language-tools 2.1 可以根据匹配到的元素推断引用类型。对一个普通 来说,开发工具能够把 searchBox.value 识别为对应的输入元素接口,而不是让你手工写一长串 DOM 类型。

这里的关键不是把 useTemplateRef 当作普通状态容器,而是让模板里的静态目标参与类型推断。vue-tsc 和 IDE 的提示来自这层关联,所以重命名时要同步修改模板键与脚本参数。
自动推断覆盖不到时,显式泛型怎么写
动态组件、非 SFC 用法或工具无法取得完整模板信息时,可以直接给出泛型。原生元素最简单:把目标 DOM 接口写在 useTemplateRef 中。
import { useTemplateRef } from 'vue'
// 动态或无法自动推断时,显式指定原生元素接口
const searchBox = useTemplateRef('searchBox')
// 使用前仍要处理尚未挂载的 null
function clearSearch() {
searchBox.value?.select()
searchBox.value && (searchBox.value.value = '')
}
显式泛型解决的是“它是什么类型”,并不改变“它什么时候存在”。模板引用在组件挂载前可能为空,引用元素被 v-if 移除后也可能重新变成 null,所以可选链或类型守卫仍然需要保留。
子组件引用要区分实例类型和公开接口
把 ref 放到子组件上时,引用目标不再是原生 DOM,而是子组件实例。对于静态导入的普通组件,可以用 InstanceType 得到实例类型,再交给 useTemplateRef。
如果子组件使用 ,它默认是私有的。父组件只有在子组件通过 defineExpose 暴露方法或状态后,才能通过模板引用访问这些成员。
如果只关心所有组件都共有的公开成员,可以使用 Vue 提供的 ComponentPublicInstance。它比具体组件类型宽,但更适合动态组件或不需要访问私有实现细节的场景。
动态组件和 v-for:类型边界要写清楚
动态组件可能在多个组件类型之间切换,不能假设引用永远是某一个具体实例。可以把候选实例组成联合类型;若只需要共同能力,则退回到 ComponentPublicInstance。
import { useTemplateRef } from 'vue'
import type { ComponentPublicInstance } from 'vue'
import Foo from './Foo.vue'
import Bar from './Bar.vue'
type FooType = InstanceType
type BarType = InstanceType
// 动态组件的引用类型覆盖两个可能的实例
const componentRef = useTemplateRef('component')
// 只依赖公共实例能力时使用更宽的类型
const publicRef = useTemplateRef('publicComponent')

在 v-for 中,Vue 3.5 也支持把同一个模板引用收集为数组。此时需要把它理解为“一组匹配元素”,不要依赖数组顺序和源数组永久一致;如果业务需要稳定对应关系,应该自己使用更明确的键或数据结构。
Vue 3.5 之前的兼容写法
useTemplateRef() 是 Vue 3.5 引入的辅助函数。更早的版本要用普通 ref 保存模板引用,并显式把初始值写成 null。如果项目还没有升级到 3.5,不能只复制新 API。
两种写法的生命周期事实相同:引用并不是创建时就指向 DOM。升级时可以先保留 null 判断,再把声明换成 useTemplateRef,最后让 IDE 或 vue-tsc 接管静态元素的推断。
一张表记住选择方式
| 场景 | 推荐写法 | 注意点 |
|---|---|---|
| Vue 3.5+ 静态原生元素 | useTemplateRef('input') | 模板 ref 键要完全匹配,类型可由语言工具推断 |
| 无法自动推断的原生元素 | useTemplateRef | 泛型只解决类型,仍要处理 null |
| 静态导入子组件 | InstanceType | 子组件需用 defineExpose 暴露成员 |
| 动态组件或只需公共能力 | ComponentPublicInstance | 类型更宽,只访问各组件共有的公开成员 |
| Vue 3.5 之前 | ref | 使用普通 ref,并保持挂载生命周期判断 |
最后检查这三个容易混淆的点
第一,useTemplateRef 的参数是模板中的引用键,不是 TypeScript 变量名;第二,自动推断依赖静态 SFC 模板与匹配的 Vue 语言工具,动态场景要准备显式泛型;第三,类型正确不等于运行时已经挂载,onMounted、v-if 和组件卸载都会让引用经历 null 状态。
把这三点分开后,模板引用的写法就比较稳定:能推断时让工具推断,不能推断时明确写出目标类型,访问任何引用时都保留生命周期边界。
相关问题
useTemplateRef 一定要在 onMounted 里使用吗?
不一定要把声明放进 onMounted,但读取 DOM 或子组件实例通常要等挂载后,并且仍需处理卸载后的 null。
为什么 useTemplateRef 的 value 还是可能为 null?
模板引用在首次渲染前还没有对应节点,使用 v-if 移除节点后也会失去引用,所以严格类型检查会要求可选链或类型守卫。
子组件 ref 为什么拿不到 script setup 里的方法?
组件默认只暴露公开接口,需要在子组件中用 defineExpose 显式暴露,并在父组件中使用对应实例类型。
-
502 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
437 收藏
-
488 收藏
-
232 收藏
-
343 收藏
-
390 收藏
-
102 收藏
-
147 收藏
-
186 收藏
-
431 收藏
-
139 收藏
-
361 收藏
-
420 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习