🔍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)进行结构灵活扩展。
相关推荐
太阳伞下的阿呆1 小时前
本地环境vue与springboot联调
前端·vue.js·spring boot
阳光是sunny1 小时前
走进微前端(1)手写single-spa核心原理
前端·javascript·vue.js
飞翔的佩奇1 小时前
基于SpringBoot+MyBatis+MySQL+VUE实现的名城小区物业管理系统(附源码+数据库+毕业论文+开题报告+部署教程+配套软件)
数据库·vue.js·spring boot·mysql·毕业设计·mybatis·小区物业管理系统
chancygcx_1 小时前
前端框架Vue3(二)——Vue3核心语法之OptionsAPI与CompositionAPI与setup
vue.js·前端框架
烛阴2 小时前
Ceil -- 从平滑到阶梯
前端·webgl
90后的晨仔2 小时前
👂 Vue 侦听器(watch)详解:监听数据的变化
前端·vue.js
90后的晨仔3 小时前
深入浅出 Vue 的 computed:不仅仅是“计算属性”那么简单!
前端·vue.js
Nan_Shu_6143 小时前
学习:入门uniapp Vue3组合式API版本(17)
前端·vue.js·学习·uni-app
止观止3 小时前
Remix框架:高性能React全栈开发实战
前端·react.js·前端框架·remix