🔍Vue 模板引用(Template Refs)全解析:当你必须操作 DOM 时

Vue 强调声明式编程,但现实中,仍有一些场景必须直接操作 DOM 或访问子组件,比如:

  • 页面加载时让某个 自动聚焦;

  • 通过第三方库(如图表、动画)操作某个 DOM 元素;

  • 让父组件控制子组件的内部方法或数据。

这时候,Vue 提供了一个优雅的解决方案 ------ 模板引用(template ref)。

本文将从基础到进阶,一步步讲透 Vue 中的 ref="xxx" 是如何工作的,特别是在组合式 API 和 Vue 3.5 中的新能力。


✨ 什么是模板引用?

模板引用是给 DOM 元素或组件打一个"标签",在 JavaScript 中就可以拿到它的真实引用。

js 复制代码
<template>
  <input ref="myInput" />
</template>

通过 ref,我们就能在 JS 中这样访问这个 DOM 元素。


🧱 一、在组合式 API 中如何访问模板引用?

Vue 3.5 起推荐使用 useTemplateRef() 辅助函数:

js 复制代码
<script setup>
import { useTemplateRef, onMounted } from 'vue'

const input = useTemplateRef('my-input')

onMounted(() => {
  input.value.focus() // 自动聚焦
})
</script>

<template>
  <input ref="my-input" />
</template>

👀 说明:

  • input.value 就是 [ ] 这个 DOM 节点;
  • 必须在 onMounted() 后访问它,因为挂载前元素还未插入 DOM;
  • 类型会自动根据 ref 绑定的 HTML 元素推断(适用于 TS 开发者)。

⏳ 二、挂载时机 & null 值

⚠️ 在 DOM 未挂载前,ref 是 null,你不能提前在

使用 watchEffect 来安全处理:

js 复制代码
watchEffect(() => {
  if (input.value) {
    input.value.focus()
  } else {
    // 尚未挂载,或已卸载(比如通过 v-if 控制)
  }
})

这种写法适用于:元素动态插入、被 v-if 控制显示等复杂情况。


🧩 三、组件上的模板引用

不仅可以引用 DOM 元素,也可以直接引用子组件实例:

js 复制代码
<script setup>
import Child from './Child.vue'
import { useTemplateRef, onMounted } from 'vue'

const childRef = useTemplateRef('child')

onMounted(() => {
  console.log('子组件实例:', childRef.value)
})
</script>

<template>
  <Child ref="child" />
</template>

🧱 父组件能访问子组件的哪些内容?

  • 若子组件使用的是选项式 API,父组件几乎能访问它的所有属性和方法;
  • 若子组件使用 :
js 复制代码
<!-- Child.vue -->
<script setup>
import { ref } from 'vue'

const count = ref(0)
const sayHi = () => console.log('Hello from child!')

defineExpose({ count, sayHi }) // 手动暴露
</script>

📦 四、在 v-for 中使用模板引用(Vue 3.5+)

Vue 3.5 开始,v-for 语境下的 ref 会自动聚合成数组!

js 复制代码
<script setup>
import { useTemplateRef, onMounted, ref } from 'vue'

const list = ref(['A', 'B', 'C'])
const itemRefs = useTemplateRef('items')

onMounted(() => {
  console.log('所有 <li> 元素:', itemRefs.value)
})
</script>

<template>
  <ul>
    <li v-for="item in list" :key="item" ref="items">
      {{ item }}
    </li>
  </ul>
</template>

🧠 注意事项:

  • itemRefs.value 是一个 DOM 元素数组;
  • 每个元素在挂载后才可访问;
  • 需使用唯一的 :key 以保持顺序一致。

🔁 五、函数形式模板引用(更灵活)

除了字符串名,你还可以使用函数形式绑定模板引用:

js 复制代码
<script setup>
import { ref } from 'vue'

const inputEl = ref(null)

function handleRef(el) {
  inputEl.value = el
}
</script>

<template>
  <input :ref="handleRef" />
</template>

函数会在:

  • 元素挂载时被调用(传入元素本身);

  • 元素卸载时再调用一次(传入 null);

✅ 优点:

  • 更灵活;
  • 可以直接绑定到组件方法;
  • 在你需要动态分发或集中管理多个引用时很好用。

🧪 六、实战:打开模态框时自动聚焦输入框

js 复制代码
<script setup>
import { ref, watch } from 'vue'
import { useTemplateRef } from 'vue'

const show = ref(false)
const inputRef = useTemplateRef('input')

watch(show, (visible) => {
  if (visible && inputRef.value) {
    inputRef.value.focus()
  }
})
</script>

<template>
  <button @click="show = true">打开模态框</button>

  <div v-if="show" class="modal">
    <input ref="input" placeholder="请输入..." />
  </div>
</template>

🎯 使用模板引用的最佳实践

用法场景 推荐方式
访问 DOM 元素 useTemplateRef('name')
动态 DOM(v-if/v-for) 配合 watchEffect 或回调式 ref
获取子组件实例 + defineExpose
多个元素(v-for) 3.5+ 中 ref 自动聚合为数组
动态绑定 使用函数形式的 :ref="fn"

🔚 总结

模板引用是 Vue 提供的一种对底层的"逃逸口",但它不应该成为主流手段。只有在以下场景中推荐使用:

✅ 需要访问 DOM 元素(如获取焦点、测量宽高)

✅ 第三方库初始化依赖 DOM

✅ 父组件需要精细控制子组件行为(用 defineExpose)

✅ 需要动态聚合 v-for 中的元素

其余场景,我们更推荐你使用:

  • props 和 emit 来传递数据;
  • computed、watch 来处理响应;
  • 插槽(slot)进行结构灵活扩展。
相关推荐
晴空雨4 分钟前
💥 React 容器组件深度解析:从 Props 拦截到事件改写
前端·react.js·设计模式
Marshall35728 分钟前
前端水印防篡改原理及实现
前端
阿虎儿20 分钟前
TypeScript 内置工具类型完全指南
前端·javascript·typescript
IT_陈寒29 分钟前
Java性能优化实战:5个立竿见影的技巧让你的应用提速50%
前端·人工智能·后端
chxii1 小时前
6.3Element UI 的表单
javascript·vue.js·elementui
张努力1 小时前
从零开始的开发一个vite插件:一个程序员的"意外"之旅 🚀
前端·vue.js
远帆L1 小时前
前端批量导入内容——word模板方案实现
前端
Codebee1 小时前
OneCode3.0-RAD 可视化设计器 配置手册
前端·低代码
chxii1 小时前
6.4 Element UI 中的 <el-table> 表格组件
vue.js·ui·elementui