登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  前端

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 类型。

Vue useTemplateRef 从模板 ref 名称推断 HTMLInputElement 的静态结构说明图
图1:结构图展示静态模板引用从 ref 名称到 HTMLInputElement 的类型关系;这是原创说明图,不是 IDE 截图或运行证据。

这里的关键不是把 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')
Vue useTemplateRef 显式泛型、组件实例和 null 生命周期边界说明图
图2:关系图对比原生元素泛型、组件实例类型与 null 生命周期边界;这是原创说明图,不是运行截图。

在 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('input')泛型只解决类型,仍要处理 null
静态导入子组件InstanceType 子组件需用 defineExpose 暴露成员
动态组件或只需公共能力ComponentPublicInstance类型更宽,只访问各组件共有的公开成员
Vue 3.5 之前ref(null)使用普通 ref,并保持挂载生命周期判断

最后检查这三个容易混淆的点

第一,useTemplateRef 的参数是模板中的引用键,不是 TypeScript 变量名;第二,自动推断依赖静态 SFC 模板与匹配的 Vue 语言工具,动态场景要准备显式泛型;第三,类型正确不等于运行时已经挂载,onMounted、v-if 和组件卸载都会让引用经历 null 状态。

把这三点分开后,模板引用的写法就比较稳定:能推断时让工具推断,不能推断时明确写出目标类型,访问任何引用时都保留生命周期边界。

相关问题

useTemplateRef 一定要在 onMounted 里使用吗?

不一定要把声明放进 onMounted,但读取 DOM 或子组件实例通常要等挂载后,并且仍需处理卸载后的 null。

为什么 useTemplateRef 的 value 还是可能为 null?

模板引用在首次渲染前还没有对应节点,使用 v-if 移除节点后也会失去引用,所以严格类型检查会要求可选链或类型守卫。

子组件 ref 为什么拿不到 script setup 里的方法?

组件默认只暴露公开接口,需要在子组件中用 defineExpose 显式暴露,并在父组件中使用对应实例类型。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>