×

Vue3 模板引用一次讲透

独孤求败 独孤求败 发表于2026-06-30 10:45:05 浏览29 评论0

抢沙发发表评论

在 Vue3 中,大多数情况下我们都不需要直接操作 DOM。

因为:

Vue 的声明式渲染已经帮我们完成了绝大多数工作。

但是在企业开发中,仍然有很多场景必须直接访问 DOM 或组件实例,例如:

  • 输入框自动聚焦
  • 获取元素宽高
  • 初始化第三方库
  • 操作滚动条
  • 播放视频
  • 调用子组件方法
  • 获取组件实例
  • 批量获取列表元素

而 Vue3 提供的模板引用(Template Refs),就是专门解决这些问题的。

本文将系统讲解 Vue3 模板引用的全部核心知识,包括:

  • ref 基础用法
  • useTemplateRef
  • DOM 引用
  • 组件引用
  • defineExpose
  • v-for 中的 ref
  • 函数式 ref
  • this.$refs
  • 常见误区
  • 企业级开发场景

看完后,你将彻底掌握 Vue3 中模板引用的使用方式。

一、什么是模板引用(Template Refs)

模板引用本质上是:

获取模板中的 DOM 元素或组件实例。

Vue 官方文档中指出:

ref 是一个特殊 attribute,用于在元素或组件挂载后获得直接引用。

例如:

<input ref="inputRef" />

这里:

inputRef

就是这个 input 元素的引用。


二、为什么需要模板引用

Vue 是声明式框架。

理论上:

我们应该避免直接操作 DOM。

但有些场景:

必须访问真实 DOM。

例如:

场景
是否需要模板引用
输入框自动聚焦
获取元素高度
初始化 ECharts
初始化地图
播放视频
调用子组件方法
普通数据显示
条件渲染


三、Vue3 中 ref 的基础使用


1、模板中定义 ref

<template>
  <input ref="inputRef" />
</template>

2、script 中获取 ref

Vue3.5 开始:

官方推荐:

useTemplateRef()

3、完整示例

<script setup>
import { useTemplateRef, onMounted } from 'vue'

const inputRef = useTemplateRef('inputRef')

onMounted(() => {
  inputRef.value.focus()
})
</script>

<template>
  <input ref="inputRef" />
</template>

组件挂载后:

输入框会自动获取焦点。


四、为什么必须在 onMounted 中访问

这是新手最容易踩坑的地方。

错误写法:

console.log(inputRef.value)

结果:

null

原因:

DOM 还没渲染。

Vue 官方明确说明:

只有组件挂载后才能访问模板引用。

因此:

必须:

onMounted(() => {})

之后再访问。


五、Vue3.5 之前的写法

在 Vue3.5 之前:

没有:

useTemplateRef()

需要手动创建 ref。


示例

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

const inputRef = ref(null)

onMounted(() => {
  inputRef.value.focus()
})
</script>

<template>
  <input ref="inputRef" />
</template>

六、模板 ref 与响应式 ref 的区别

这是很多人容易混淆的地方。


响应式 ref

const count = ref(0)

用于:

响应式数据。


模板 ref

const inputRef = useTemplateRef('inputRef')

用于:

获取 DOM 或组件实例。


本质区别

类型
用途
ref(0)
响应式数据
模板 ref
DOM/组件引用


七、操作 DOM 的常见场景


1、输入框聚焦

<script setup>
import { useTemplateRef, onMounted } from 'vue'

const inputRef = useTemplateRef('input')

onMounted(() => {
  inputRef.value.focus()
})
</script>

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

2、获取元素高度

onMounted(() => {
  console.log(boxRef.value.offsetHeight)
})

3、滚动到底部

listRef.value.scrollTop = listRef.value.scrollHeight

4、播放视频

videoRef.value.play()

5、初始化第三方库

例如:

  • ECharts
  • Monaco Editor
  • 地图 SDK
  • Canvas
onMounted(() => {
  echarts.init(chartRef.value)
})

八、watchEffect 中使用模板 ref

Vue 官方特别说明:

模板 ref 可能为 null。

例如:

<div v-if="show" ref="box"></div>

当:

show = false

时:

元素被销毁。

ref 会重新变成:

null

正确写法

watchEffect(() => {
  if (boxRef.value) {
    console.log(boxRef.value)
  }
})

九、组件上的 ref

模板 ref 不仅能获取 DOM。

还能获取:

子组件实例。


示例

子组件

<template>
  <div>子组件</div>
</template>

父组件

<script setup>
import { useTemplateRef, onMounted } from 'vue'
import Child from './Child.vue'

const childRef = useTemplateRef('childRef')

onMounted(() => {
  console.log(childRef.value)
})
</script>

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

此时:

childRef.value

就是子组件实例。


十、defineExpose 详解

这是 Vue3 企业开发中的重点。


问题

如果子组件使用:

<script setup>

默认情况下:

父组件无法访问子组件内部数据。

这是 Vue3 的“组件私有化”。


十一、defineExpose 暴露数据


子组件

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

const count = ref(0)

const increment = () => {
  count.value++
}

defineExpose({
  count,
  increment
})
</script>

父组件

<script setup>
import { useTemplateRef, onMounted } from 'vue'
import Child from './Child.vue'

const childRef = useTemplateRef('child')

onMounted(() => {
  childRef.value.increment()
})
</script>

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

为什么需要 defineExpose

Vue 官方指出:

<script setup> 组件默认是私有的。

必须:

defineExpose()

才能对父组件暴露内容。


十二、组件 ref 的风险

Vue 官方明确提醒:

组件 ref 会导致父子组件强耦合。

因此:

优先使用:

  • props
  • emit

只有必要时:

再使用组件 ref。


十三、Options API 中的 $refs

如果不是 Composition API

还可以使用:

this.$refs

示例

<script>
export default {
  mounted() {
    this.$refs.input.focus()
  }
}
</script>

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

十四、v-for 中的模板引用

这是 Vue3 中非常重要的知识点。


示例

<script setup>
import { useTemplateRef, onMounted } from 'vue'

const itemRefs = useTemplateRef('items')

onMounted(() => {
  console.log(itemRefs.value)
})
</script>

<template>
  <li
    v-for="item in 5"
    ref="items"
  >
    {{ item }}
  </li>
</template>

结果

itemRefs.value

会变成:

[
  li,
  li,
  li
]

也就是说:

ref 会自动变成数组。


十五、ref 数组顺序问题

Vue 官方特别说明:

ref 数组不保证和源数组顺序一致。

这是很多开发者忽略的问题。

社区讨论中也有人专门提到:

使用 key 后可能出现顺序变化。

因此:

不要依赖:

itemRefs[0]

一定对应:

list[0]

十六、函数式 ref(Function Refs)

除了字符串:

ref 还可以绑定函数。


示例

<input :ref="setRef" />

script

const setRef = (el) => {
  console.log(el)
}

特点

函数 ref:

会在每次组件更新时调用。

元素销毁时:

参数会变成:

null

十七、函数 ref 的高级玩法

函数 ref 最大的优势:

是灵活。

例如:

动态存储多个 DOM。


示例

<div
  v-for="item in list"
  :ref="el => setItemRef(el, item.id)"
></div>

script

const refsMap = {}

const setItemRef = (el, id) => {
  refsMap[id] = el
}

这样:

就可以通过 id 获取 DOM。

企业级项目中非常常见。


十八、模板 ref 与 id 的区别

很多初学者喜欢:

document.getElementById()

但 Vue 官方和社区都更推荐:

Template Refs。


为什么

因为:

id 是全局的。

而:

模板 ref 是组件局部的。

不会出现:

  • id 冲突
  • 组件复用问题
  • SSR 问题

十九、企业开发中的典型场景


1、ECharts 初始化

onMounted(() => {
  chart = echarts.init(chartRef.value)
})

2、表格滚动

tableRef.value.scrollTop = 0

3、调用弹窗组件方法

dialogRef.value.open()

4、上传组件触发点击

uploadRef.value.click()

社区中也有人讨论过:

Vue3 中模板 ref 本质仍然是 HTMLElement。


二十、模板 ref 的常见误区


1、初始化直接访问

错误:

console.log(inputRef.value)

正确:

onMounted(() => {})

2、v-if 导致 ref 为 null

<div v-if="show" ref="box"></div>

元素销毁后:

ref 会变 null。


3、滥用组件 ref

很多场景:

应该:

  • props
  • emit

而不是:

childRef.value.xxx()

4、把模板 ref 当响应式数据

模板 ref 的主要用途:

是访问 DOM。

不是存业务数据。


二十一、Vue2 与 Vue3 的区别


Vue2

主要使用:

this.$refs

Vue3

推荐:

  • Composition API
  • useTemplateRef
  • defineExpose

Vue3 的模板引用:

更加类型安全, 更加符合组合式 API 风格。


二十二、总结

Vue3 模板引用是:

获取 DOM 与组件实例的重要机制。

核心能力包括:

功能
作用
ref
获取 DOM
useTemplateRef
Vue3.5 官方推荐写法
组件 ref
获取子组件实例
defineExpose
对外暴露组件能力
v-for ref
批量获取元素
Function Ref
灵活控制引用

企业开发中:

模板引用主要用于:

  • DOM 操作
  • 第三方库初始化
  • 调用子组件方法
  • 获取元素信息
  • 动画与滚动控制

但一定要记住:

Vue 推荐声明式开发。

模板引用只是:

“必要时直接操作 DOM 的工具”。

不要把它当成主要开发方式。


群贤毕至

访客