10Vue3 模板引用 (Template Refs)
概念
模板引用是 Vue 3 中直接访问 DOM 元素或组件实例的方式。通过特殊的 ref 属性,可以在 JavaScript 中获取对模板中元素的直接引用。
基本用法
1. 创建和使用 ref
<template>
<div>
<!-- 在元素上使用 ref -->
<input ref="inputRef" type="text" />
<button @click="focusInput">聚焦输入框</button>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
// 创建 ref,名称需要与模板中的 ref 属性值一致
const inputRef = ref(null)
const focusInput = () => {
// 访问 DOM 元素
inputRef.value.focus()
}
onMounted(() => {
// 组件挂载后,ref 才会被赋值
console.log(inputRef.value) // <input type="text">
})
</script>
2. 在选项式 API 中使用
<template> <div ref="divRef">这是一个 div 元素</div> </template> <script> export default { mounted() { // 通过 this.$refs 访问 console.log(this.$refs.divRef) // <div>这是一个 div 元素</div> } } </script>
引用组件实例
1. 引用子组件
<!-- 父组件 -->
<template>
<div>
<ChildComponent ref="childRef" />
<button @click="callChildMethod">调用子组件方法</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
import ChildComponent from './ChildComponent.vue'
const childRef = ref(null)
const callChildMethod = () => {
// 调用子组件的方法
childRef.value.someMethod()
// 访问子组件的属性
console.log(childRef.value.someProperty)
}
</script>
<!-- 子组件 ChildComponent.vue -->
<script setup>
import { ref, defineExpose } from 'vue'
const someProperty = ref('子组件数据')
const someMethod = () => {
console.log('子组件方法被调用')
}
// 需要显式暴露,父组件才能访问
defineExpose({
someProperty,
someMethod
})
</script>
动态 ref
<template> <div v-for="item in items" :key="item.id"> <!-- 动态 ref --> <div :ref="el => { if (el) divRefs[item.id] = el }"> {{ item.name }} </div> </div> </template> <script setup> import { ref, reactive } from 'vue' const items = ref([ { id: 1, name: 'Item 1' }, { id: 2, name: 'Item 2' } ]) // 使用对象或 Map 存储多个 ref const divRefs = reactive({}) </script>
ref 函数
<template>
<div>
<!-- 使用函数形式的 ref -->
<input :ref="setInputRef" type="text" />
</div>
</template>
<script setup>
import { ref, onBeforeUpdate, onUpdated } from 'vue'
let inputElement = null
const setInputRef = (el) => {
inputElement = el
}
onBeforeUpdate(() => {
// 在更新前清除 ref
inputElement = null
})
onUpdated(() => {
// 更新后重新设置 ref
console.log(inputElement)
})
</script>
组合式 API 中的使用模式
1. 使用 ref 数组
<template>
<div>
<div
v-for="i in 3"
:key="i"
:ref="el => { if (el) elementRefs[i-1] = el }"
>
元素 {{ i }}
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const elementRefs = ref([])
onMounted(() => {
console.log(elementRefs.value) // [div, div, div]
})
</script>
2. ref 在响应式对象中
<script setup> import { reactive, onMounted } from 'vue' const state = reactive({ inputRef: null }) onMounted(() => { // 访问 reactive 对象中的 ref if (state.inputRef) { state.inputRef.focus() } }) </script> <template> <input ref="el => state.inputRef = el" type="text" /> </template>
最佳实践
-
访问时机:ref 在组件挂载后才可用,使用
onMounted生命周期钩子确保安全访问 -
响应式:ref 本身是响应式的,但 ref 指向的 DOM 元素不是响应式的
-
类型安全:在 TypeScript 中为 ref 提供类型注解
// TypeScript 示例 import { ref } from 'vue' // HTML 元素 ref const inputRef = ref<HTMLInputElement | null>(null) // 组件 ref(假设子组件有特定类型) const childRef = ref<InstanceType<typeof ChildComponent> | null>(null)
避免过度使用:优先使用 Vue 的声明式数据驱动,必要时才使用 ref
常见应用场景
-
聚焦输入框
-
触发动画
-
集成第三方 DOM 库
-
测量元素尺寸
-
手动触发组件方法
-
文件上传控件
<template>
<div>
<!-- 文件上传示例 -->
<input
ref="fileInput"
type="file"
style="display: none"
@change="handleFileChange"
/>
<button @click="triggerFileInput">选择文件</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
const fileInput = ref(null)
const triggerFileInput = () => {
fileInput.value.click()
}
const handleFileChange = (event) => {
const file = event.target.files[0]
// 处理文件
}
</script>
模板引用提供了直接操作 DOM 的能力,但在 Vue 应用中应谨慎使用,优先考虑声明式的数据驱动方式

浙公网安备 33010602011771号