🔥🔥🔥Vue 3.5 核弹级小补丁:useTemplateRef 让 ref 一夜失业?

关键词:Vue 3.5、Composition API、useTemplateRef、模板引用、TypeScript

1. 开场白:被 ref 支配的恐惧

"哥,我只是想拿到 input 自己 focus 一下,怎么又 undefined 了?"

"你把 ref 名写错了,模板里是 ref="inputEl",脚本里写成 inputRef 能行?"

------以上对话每天在全球 420 万个 Vue 组件里循环播放。

Vue 3.0 ~ 3.4 的 ref 在模板引用场景里一直有几个老毛病:

痛点 现场翻车
命名强耦合 模板 ref="foo" ↔ 脚本 const foo = ref(null),写错一个字母就全剧终
类型 = any 默认推断不出是 HTMLInputElement,IDE 全程装死
动态 ref 几乎无解 v-for:ref="el-${index}",脚本里根本拿不到
逻辑抽不出去 想在 useFocus() 组合式函数里封装?先学会"影分身"

于是,Vue 3.5 悄悄塞给我们一个新 API------useTemplateRef。今天这篇小作文,带你从"以前哭着写"到"现在笑着删"。


2. 以前是怎么"跪着"写代码

2.1 最原始的 ref

vue 复制代码
<template>
  <input ref="username" />
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue'

// 变量名必须和模板完全一致
const username = ref<HTMLInputElement | null>(null)

onMounted(() => {
  // 类型收窄全靠 ?
  username.value?.focus()
})
</script>

槽点

  1. 万一模板改名,脚本忘记改------连夜加班。
  2. 新人不写 <HTMLInputElement | null>,默认 any 一路飘红。

2.2 v-for 里想动态拿 ref?

vue 复制代码
<template>
  <div v-for="(item, i) in list" :key="i">
    <input :ref="`input-${i}`" />
  </div>
</template>

<script setup>
import { ref } from 'vue'

// 不好意思,官方文档直接劝退:
// "动态 ref 不建议在 <script setup> 里使用"
</script>

得,手写 Map 吧,直接回到 jQuery 时代。


3. Vue 3.5 的"真香"时刻:useTemplateRef 登场

3.1 签名一览

ts 复制代码
function useTemplateRef<T = Element>(
  key: string
): Readonly<Ref<T | null>>
  • 参数就是模板里 ref 属性的值;
  • 返回值是只读Ref,自动推断类型;
  • 可在组合式函数里随便调,不依赖组件作用域变量名

3.2 最简示例

vue 复制代码
<template>
  <input ref="username" />
</template>

<script setup lang="ts">
import { useTemplateRef, onMounted } from 'vue'

// 变量名随意,类型自动是 HTMLInputElement | null
const inputEl = useTemplateRef('username')

onMounted(() => {
  inputEl.value?.focus() // 有提示,有收窄,有幸福
})
</script>

改动点

  1. 模板 ref="username" 纹丝不动;
  2. 脚本端想叫 inputElelfoo 随你开心;
  3. 鼠标放上去,IDE 秒出 HTMLInputElement

4. 实战:三个高频场景"前后对比"

4.1 场景 A:表单自动聚焦

旧写法 新写法
`const username = ref<HTMLInputElement null>(null)`
命名写错 = undefined 随意命名,零耦合
类型手动写 全自动推断

4.2 场景 B:动态列表里操作某一个 input

vue 复制代码
<template>
  <div v-for="(item, index) in items" :key="index">
    <input :ref="`input-${index}`" />
    <button @click="focus(index)">Focus</button>
  </div>
</template>

<script setup lang="ts">
import { useTemplateRef } from 'vue'

const focus = (index: number) => {
  const el = useTemplateRef<HTMLInputElement>(`input-${index}`)
  el.value?.focus()
}
</script>

过去 :得自己维护 refMap: Map<string, El>,渲染函数里再手动赋值。
现在 :一句 useTemplateRef 搞定,循环 1 万条也不怕

4.3 场景 C:把"聚焦"逻辑抽到组合式函数

ts 复制代码
// hooks/useFocus.ts
import { useTemplateRef, nextTick } from 'vue'

export function useFocus(refKey: string) {
  const target = useTemplateRef<HTMLInputElement>(refKey)

  const focus = () => nextTick(() => target.value?.focus())

  return { focus }
}
vue 复制代码
<template>
  <input ref="email" />
  <button @click="focus">Focus Email</button>
</template>

<script setup lang="ts">
import { useFocus } from '@/hooks/useFocus'

const { focus } = useFocus('email')
</script>

旧 API 做不到ref 必须声明在组件作用域,组合式函数里无法提前声明变量名
新 APIuseTemplateRef 只是运行时查 key,彻底解耦


5. 源码小贴士:为什么它这么快?

Vue 3.5 在编译阶段把模板里的静态 ref 收集到 setupRenderEffectref 队列里;
useTemplateRef(key) 只是从队列里读对应的 vnode ref不触发额外依赖收集,性能 ≈ 原生。


6. 版本 & 兼容

条目 要求
Vue 版本 ≥ 3.5.0 (2024-09 发布)
Volar / VSCode 插件 ≥ 2.1.0 才有类型提示
旧项目升级 完全向后兼容,可渐进替换

7. 踩坑指南

  1. 别和 v-if 一起乱舞

    节点被卸载后 useTemplateRef 返回的 value 会重置为 null,记得加 ?.

  2. 只读!别妄想赋值

    ts 复制代码
    const el = useTemplateRef('foo')
    el.value = document.createElement('div') // ❌ 编译时报错
  3. key 必须和模板完全匹配

    模板 :ref="input-1",脚本里写 useTemplateRef('input1') 会找不到。


8. 结论:一句话总结

useTemplateRef 不是银弹,但在"模板引用"这个细分战场,它让命名自由、类型安全、动态 ref、逻辑复用 四连击,写完直接删 30% 防御性代码香到隔壁 React 组都过来问链接


9. 互动时间

你在哪些奇葩场景里被 ref 坑过?

评论区甩链接,一起给 useTemplateRef 上香~

如果本文帮你少踩一个坑,记得点个 Star 哦!

相关推荐
我只会写Bug啊1 小时前
Vue文件预览终极方案:PNG/EXCEL/PDF/DOCX/OFD等10+格式一键渲染,开源即用!
前端·vue.js·pdf·excel·预览
扯蛋4382 小时前
LangChain的学习之路( 一 )
前端·langchain·mcp
Mr.Jessy3 小时前
Web APIs学习第一天:获取 DOM 对象
开发语言·前端·javascript·学习·html
午安~婉3 小时前
javaScript八股问题
开发语言·javascript·原型模式
西西学代码3 小时前
Flutter---个人信息(5)---持久化存储
java·javascript·flutter
芝麻开门-新起点3 小时前
flutter 生命周期管理:从 Widget 到 State 的完整解析
开发语言·javascript·ecmascript
ConardLi4 小时前
Easy Dataset 已经突破 11.5K Star,这次又带来多项功能更新!
前端·javascript·后端
冴羽4 小时前
10 个被严重低估的 JS 特性,直接少写 500 行代码
前端·javascript·性能优化
rising start4 小时前
四、CSS选择器(续)和三大特性
前端·css
一 乐4 小时前
高校后勤报修系统|物业管理|基于SprinBoot+vue的高校后勤报修系统(源码+数据库+文档)
java·前端·javascript·数据库·vue.js·毕设