🔥🔥🔥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 哦!

相关推荐
拜无忧8 小时前
【案例】可视化模板,驾驶舱模板,3x3,兼容移动
前端·echarts·数据可视化
向葭奔赴♡8 小时前
前端框架学习指南:提升开发效率
前端·javascript·vue.js
小许哥8 小时前
如何把微信小程序转换成支付宝小程序
前端
汤姆Tom8 小时前
CSS 最佳实践与规范
前端·css
一个不爱写代码的瘦子8 小时前
Map、weakMap和Set、weakSet
前端·javascript
认真的编剧8 小时前
freecodecamp全通关之CSS画喵咪
前端
_AaronWong8 小时前
Electron视频黑屏之谜:从H265编码到GPU禁用的深度排查
前端·electron·视频编码
前端灵派派8 小时前
openlayer点击切换图标
前端
Icoolkj8 小时前
npm、npx、pnpm 深度解析:从原理到实战的全方位指南
前端·npm·node.js