一、模板引用基础概念

模板引用是 Vue3 中直接访问 DOM 元素或子组件实例的特殊机制,通过 ref 属性实现。与 Vue2 不同,Vue3 的组合式 API 提供了更灵活的引用方式。

1.1 基本用法

<template>
  <input ref="myInput" type="text">
</template>

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

// 声明与模板中 ref 同名的响应式引用
const myInput = ref(null)

onMounted(() => {
  myInput.value.focus() // 挂载后自动聚焦输入框
})
</script>

特性说明:

  • 引用变量名必须与模板中的 ref 属性值完全匹配
  • 通过 .value 访问 DOM 元素(组合式 API 特性)
  • 必须在 onMounted 生命周期后才能访问引用

二、动态引用进阶技巧

2.1 动态引用名称

通过函数式语法实现动态绑定:

<template>
  <div v-for="item in 5" :key="item">
    <div :ref="(el) => setItemRef(el, item)">
      Item {{ item }}
    </div>
  </div>
</template>

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

const itemRefs = ref([])

const setItemRef = (el, index) => {
  if (el) itemRefs.value[index] = el
}
</script>

注意事项:

  • v-for 中的引用会生成数组,但顺序不保证与数据源一致
  • 动态引用函数会在每次 DOM 更新时调用

2.2 组件引用

访问子组件实例:

<!-- 父组件 -->
<template>
  <ChildComponent ref="childRef" />
</template>

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

const childRef = ref(null)
</script>

<!-- 子组件 ChildComponent.vue -->
<script setup>
import { ref, defineExpose } from 'vue'

const internalData = ref('secret')
const publicMethod = () => console.log('暴露的方法')

// 必须显式暴露才能被父组件访问
defineExpose({
  publicMethod
})
</script>

三、常见应用场景

3.1 表单焦点管理

<template>
  <form @submit.prevent="handleSubmit">
    <input ref="emailInput" type="email">
    <button>提交</button>
  </form>
</template>

<script setup>
const emailInput = ref(null)

const handleSubmit = () => {
  if (!emailInput.value.value) {
    emailInput.value.focus()
  }
}
</script>

3.2 第三方库集成

<template>
  <div ref="chartContainer"></div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import * as echarts from 'echarts'

const chartContainer = ref(null)
let chartInstance = null

onMounted(() => {
  chartInstance = echarts.init(chartContainer.value)
  // 初始化图表...
})

onUnmounted(() => {
  chartInstance?.dispose()
})
</script>

四、常见问题与解决方案

4.1 引用值为 null

原因:在组件挂载前访问引用
解决:使用 onMounted 生命周期钩子

4.2 动态组件引用丢失

现象:切换动态组件时引用失效
解决:使用函数式引用绑定

<template>
  <component :is="currentComponent" :ref="setDynamicRef">
</template>

<script setup>
const dynamicRef = ref(null)
const setDynamicRef = (el) => {
  dynamicRef.value = el
}
</script>

4.3 TypeScript 类型定义

// DOM 元素类型
const inputRef = ref<HTMLInputElement | null>(null)

// 组件类型
const childRef = ref<InstanceType<typeof ChildComponent> | null>(null)

五、最佳实践建议

  1. 最小化 DOM 操作:优先使用 Vue 的声明式语法
  2. 引用命名规范:使用 xxxRef 后缀提高可读性
  3. 内存管理:及时清理第三方库实例
  4. 类型安全:使用 TypeScript 增强类型检查
  5. 性能优化:避免在模板中频繁创建引用函数

通过掌握这些技巧,您可以更高效地处理需要直接操作 DOM 或组件实例的复杂场景。实际开发中,建议结合 Vue Devtools 进行引用调试。