文章目录

Vue3 useTemplateRef获取模板引用的组合式API

发布于 2026-05-01 12:18:22 · 浏览 13 次 · 评论 0 条

Vue3 useTemplateRef获取模板引用的组合式API

Vue 3.5 版本引入了 useTemplateRef API,它提供了一种更符合直觉且类型安全的方式来获取 DOM 元素或组件实例。相比旧版的 ref(null) 写法,新 API 减少了冗余代码,并自动推导模板引用的类型。以下是基于 Vue 3.5+ 的详细操作指南。


1. 环境准备与基础用法

在使用此 API 之前,必须确保项目依赖的 Vue 版本不低于 3.5。

  1. 打开终端,运行以下命令检查当前 Vue 版本。
    npm list vue
  2. 若版本低于 3.5,执行升级命令。
    npm install vue@latest

useTemplateRef 的核心逻辑在于“字符串匹配”。它在模板中寻找与传入参数同名的 ref 属性,并自动建立连接。

  1. 创建一个新的 Vue 组件文件 TemplateRefDemo.vue

  2. 编写模板代码,在目标元素上添加 ref 属性并赋予一个唯一的字符串名称(例如 "inputRef")。

    <script setup lang="ts">
    import { useTemplateRef } from 'vue'
    
    // 步骤 5:调用 API 并传入模板中定义的字符串名称
    const inputRef = useTemplateRef<HTMLInputElement>('inputRef')
    
    // 步骤 6:定义聚焦方法
    const focusInput = () => {
      // 注意:通过 .value 访问 DOM 元素
      if (inputRef.value) {
        inputRef.value.focus()
      }
    }
    </script>
    
    <template>
      <div>
        <!-- 这里的 ref 字符串 "inputRef" 必须与上方 useTemplateRef 的参数一致 -->
        <input ref="inputRef" type="text" placeholder=" focus me" />
    
        <button @click="focusInput">聚焦输入框</button>
      </div>
    </template>

2. 对比旧方案与新方案

为了理解 useTemplateRef 的优势,我们需要将其与 Vue 3.5 之前的通用写法进行对比。下表展示了两种方案在实现相同功能时的差异。

特性 旧方案 (Generic Ref) 新方案 (useTemplateRef)
变量定义 需显式声明 const inputRef = ref(null) 直接调用 const inputRef = useTemplateRef('input')
类型泛型 需手动指定 ref<HTMLInputElement \| null>(null) 自动根据模板上下文推导,或简写为 useTemplateRef<HTMLInputElement>
匹配逻辑 变量名必须与模板 ref 名完全一致 函数参数字符串必须与模板 ref 名一致
初始化值 必须传入 null 无需传入初始值

3. 处理组件引用

除了获取原生 DOM 元素,useTemplateRef 同样适用于获取子组件实例,并调用其暴露的方法。

  1. 创建子组件 ChildComponent.vue,并使用 defineExpose 暴露内部方法。

    <script setup lang="ts">
    const childMethod = () => {
      console.log('子组件方法被调用')
    }
    
    // 暴露方法给父组件
    defineExpose({
      childMethod
    })
    </script>
    
    <template>
      <div>我是子组件</div>
    </template>
  2. 回到父组件,引入 ChildComponent

  3. 使用 useTemplateRef 获取组件实例。

    <script setup lang="ts">
    import { useTemplateRef } from 'vue'
    import ChildComponent from './ChildComponent.vue'
    
    // 获取组件实例,类型为 InstanceType<typeof ChildComponent>
    const childRef = useTemplateRef<InstanceType<typeof ChildComponent>>('childComp')
    
    const callChild = () => {
      // 安全调用:检查实例是否存在
      childRef.value?.childMethod()
    }
    </script>
    
    <template>
      <ChildComponent ref="childComp" />
      <button @click="callChild">调用子组件方法</button>
    </template>

4. 利用 TypeScript 增强类型安全

useTemplateRef 支持泛型参数,这为复杂的 DOM 结构或组件实例提供了精确的类型提示。

  1. 打开组件的 <script> 标签。

  2. 在调用 useTemplateRef 时,传入具体的类型作为泛型参数 T

    例如,获取一个 VideoElement

    import { useTemplateRef } from 'vue'
    
    // 显式指定泛型为 HTMLVideoElement
    const videoRef = useTemplateRef<HTMLVideoElement>('videoPlayer')
    
    const playVideo = () => {
      if (videoRef.value) {
        // 此时 videoRef.value 会自动补全 video 元素的属性和方法
        videoRef.value.play()
      }
    }

    如果不传泛型,TypeScript 也会尝试根据 ref 在模板中绑定的元素标签进行自动推导,但在组件引用或复杂场景下,显式声明泛型是最稳妥的做法。


5. 生命周期与访问时机

虽然 useTemplateRef 简化了引用获取,但 DOM 元素的挂载时机依然遵循 Vue 的生命周期规则。在组件挂载完成前,ref 的值始终为 null

  1. 避免setup() 顶层直接访问 useTemplateRef 返回的 value

    // ❌ 错误:此时元素尚未挂载,输出 null
    console.log(inputRef.value) 
  2. 使用 onMounted 钩子确保 DOM 已渲染。

    import { onMounted } from 'vue'
    
    onMounted(() => {
      // ✅ 正确:此时元素已存在
      console.log(inputRef.value) 
    })
  3. 使用 watchwatchEffect 监听 ref 的变化,适用于需要响应式处理 DOM 变化的场景(例如 v-if 导致元素销毁与重建)。

    import { watchEffect } from 'vue'
    
    watchEffect(() => {
      if (inputRef.value) {
        // 每次 inputRef 变为非空时都会执行
        inputRef.value.focus()
      }
    })

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文