Vue.js: Refs 与 DOM 操作

最后更新:2026-10-20

Template Refs 让你直接访问 DOM 元素或子组件实例——比如调用 input.focus()、访问组件方法。Vue 3.5 引入了更强大的 useTemplateRef 组合式 API,配合 TypeScript 类型推断。

Template Refs 是"应急通道"——Vue 推荐用 ref/reactive/props/emit 解决大部分问题,Template Refs 只在需要直接操作 DOM 或组件实例时用。

1. 你将学到


2. 一个登录表单的"自动聚焦"难题

(1) 痛点:modal 打开后 input 怎么自动 focus?

Alice 构建了一个登录弹窗,需要自动聚焦用户名输入框:

JS
// ❌ 翻车版:直接 querySelector
onMounted(() => {
  const input = document.querySelector('.username-input')
  input.focus()  // ❌ 不符合 Vue 理念
})

Vue 理念:避免直接操作 DOM。改用 Template Refs。

产品经理 Charlie:

"Alice,弹窗打开后,用户名输入框应该自动聚焦,这样用户就能直接开始输入。"

(2) Vue Template Refs 解法

(3) 收益

使用 Template Refs 后:


3. ref 基础用法

(1) 字符串 ref(Vue 2 风格,已废弃)

JS
// ❌ 不推荐:字符串 ref
export default {
  mounted() {
    this.$refs.input.focus()
  }
}

(2) 变量 ref(Vue 3 推荐)

(3) 5 大基本操作

JS
// 1. 访问 DOM 元素
inputRef.value  // <input> 元素

// 2. 调用 DOM API
inputRef.value.focus()
inputRef.value.blur()
inputRef.value.select()
inputRef.value.scrollIntoView()

// 3. 读取/修改 DOM 属性
inputRef.value.value  // input 的 value
inputRef.value.disabled  // disabled 属性
inputRef.value.style.color = 'red'  // 修改样式

// 4. 监听 DOM 事件(不推荐,用 @event)
inputRef.value.addEventListener('focus', handler)

// 5. 访问子组件实例(详见 17.4)
childRef.value.someMethod()

▶ 示例:登录表单自动聚焦

▶ 试一试

输出:

TEXT
表单里两个输入框(占位符 Username / Password)和一个绿色「Login」按钮,下方灰色小字「打开页面自动聚焦到 Username 输入框」。
页面打开时光标已经在 Username 输入框里,可以直接打字。
填完点 Login → 控制台打印「Login: 用户名 密码」,页面不会刷新(@submit.prevent)。

▶ 示例:自动滚动到底部(聊天框)

▶ 试一试

输出:

TEXT
一个固定 150px 高、可滚动的消息框,初始 3 条:「👋 欢迎来到聊天室」「这是历史消息 2」「这是历史消息 3」,下面是输入框和「发送」按钮。
输入文字后点「发送」或按回车 → 新消息追加到列表末尾、输入框清空、消息框自动滚到最底部。
输入为空时点发送不会有任何变化。

4. useTemplateRef(Vue 3.5+ 推荐)

(1) 为什么需要 useTemplateRef?

变量 ref 在 <script setup> 中需要起 2 个名字(模板 ref + 变量),容易不一致。useTemplateRef 用一个名字搞定,TypeScript 推断更强。

(2) 5 大优势

优势 说明
类型推断 TypeScript 自动知道是 HTMLInputElement
重命名安全 改一处名字 IDE 同步
避免名字错配 模板 ref 和变量名不一致会报错
简化 setup 无需 const inputRef = ref(null)
更好的 DevTools Vue DevTools 5.x 支持

(3) 完整对比

VUE
<!-- 旧写法:变量 ref -->
<template>
  <input ref="usernameInput">
</template>

<script setup>
const { ref, onMounted } = Vue
const inputRef = ref(null)  // 名字可能和模板不一致
onMounted(() => inputRef.value.focus())
</script>

<!-- 新写法:useTemplateRef(Vue 3.5+ 推荐)-->
<template>
  <input ref="usernameInput">
</template>

<script setup>
const { useTemplateRef, onMounted } = Vue
const inputRef = useTemplateRef('usernameInput')  // 一个名字搞定
onMounted(() => inputRef.value.focus())
</script>

5. 访问子组件实例

(1) defineExpose 暴露方法

(2) 父组件访问

VUE
<template>
  <MyInput ref="myInputRef" v-model="searchQuery" />
  <button @click="focusInput">Focus Input</button>
</template>

<script setup>
const { ref } = Vue
import MyInput from './MyInput.vue'

const searchQuery = ref('')
const myInputRef = ref(null)

function focusInput() {
  myInputRef.value.focus()  // 调用子组件暴露的方法
  myInputRef.value.select() // 也可以链式调用
}
</script>

(3) 5 大使用场景

场景 子组件暴露 父组件调用
表单聚焦 focus() inputRef.focus()
清空表单 clear() formRef.clear()
重新加载数据 reload() tableRef.reload()
打开弹窗 open() modalRef.open()
提交表单 submit() formRef.submit()

6. v-for 中的 ref 数组

(1) 基础用法

(2) 动态 ref(v-for 动态数量)

(3) 5 大注意事项

注意点 说明
数组顺序 与 v-for 数据顺序一致
响应式 ref 数组变化需要 watch
条件渲染 v-if 后 ref 可能不更新
动态数量 用对象或函数式 ref
性能 大量 ref(100+)会拖慢渲染

7. 综合演练:父子组件 ref 校验

▶ 示例:父组件调用子组件方法(defineExpose)

▶ 展开运行示例(69 行)
▶ 试一试

输出:

TEXT
页面上是两个输入框(Email、Password (≥6位))和「Submit」按钮,初始没有任何提示文字。
空着点 Submit → 出现红色「❌ Invalid email」,光标跳到 Email 框。
填 a@b.com、密码填 123 再提交 → 红色「❌ Invalid password」;邮箱和 6 位以上密码都填好 → 出现绿色「✅ Validation passed!」。

(1) 📊 5 个常见错误速查

错误 现象 解决
模板 ref 拼错 ref.value 是 null 检查 ref 名字一致
在 setup 顶层访问 ref.value 是 null 用 onMounted
弹窗内访问 不工作 用 nextTick 等 DOM
v-for ref 数组访问 索引错位 用 :key 稳定顺序
跨组件 ref undefined 子组件用 defineExpose

(2) 📊 5 大 ref 类型对比

类型 例子 适用
DOM 元素 ref="inputRef" → HTMLInputElement 访问 input/div
组件实例 ref="childRef" → 组件实例 调子组件方法
v-for 数组 ref="itemRefs" → Array 列表项访问
函数式 ref :ref="el => ..." → 单个元素 动态数量
字符串 ref ref="name" → this.$refs Vue 2 风格(不推荐)

❓ 常见问题

Q 什么时候用 Template Refs?
A 4 种情况:(1) DOM 操作(focus/scroll/canvas);(2) 调子组件方法;(3) 集成第三方库(ECharts/Mapbox);(4) 测量元素大小。其他场景用 ref/reactive/props。
Q useTemplateRef 必须在 Vue 3.5+ 用吗?
A 是的。Vue 3.5+ 才支持。Vue 3.4 及以下用变量 ref。TypeScript 类型推断更优。
Q Template Refs 和 useRef(React)区别?
A React useRef 返回 mutable ref,Vue 模板 ref 返回 ref 对象。Vue 3.5+ useTemplateRef 更接近 React useRef 的 API。
Q 子组件需要 expose 哪些方法?
A 只 expose 父组件真的需要的方法(如 focus / clear / validate)。其他内部方法不要 expose(封装原则)。
Q v-for 中的 ref 数组什么时候更新?
A 每次 v-for 重新渲染时更新。v-if 切换也会更新。watch ref 数组可以响应变化。
Q Template Refs 和 provide/inject 冲突吗?
A 不冲突。Template Refs 用于"父访问子",provide/inject 用于"跨层级共享数据"。可组合使用。
Q 怎么在 TypeScript 中给 ref 加类型?
A 用 useTemplateRef<HTMLInputElement>('usernameInput')。或 ref<HTMLInputElement | null>(null)。

📖 小节


📝 作业

  1. 基础题(难度⭐) 实现一个简单的自动聚焦表单:

    • 1 个 input + 1 个 button
    • 页面加载后 input 自动 focus
    • 点击 button 后 button 文字改为 "Submitted" 并 disable
  2. 进阶题(难度⭐⭐) 实现一个自动滚动到底部的聊天框:

    • 消息列表(div 容器)
    • 输入框 + 发送按钮
    • 发送后消息添加到列表,自动滚动到底
    • v-for 渲染消息,ref 数组管理 DOM
  3. 挑战题(难度⭐⭐⭐) 实现完整的"父子组件 + Template Refs"系统:

    1. FormValidator 子组件:暴露 validate() 和 reset() 方法
    2. LoginPage 父组件:调用 validate 校验,失败显示错误
    3. 5 个 input 字段(username/email/password/phone/captcha)
    4. useTemplateRef(Vue 3.5+)+ TypeScript 强类型
    5. 弹窗打开时自动 focus 第一个 input
    6. 失败时 focus 到第一个错误字段
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏