当前位置: 首页 > news >正文

4.6 Vue 3 中的模板引用 (Template Refs)

在 Vue 3 中,ref 是一个核心的响应式 API,但它在模板中还有另一个非常重要的用途:获取对 DOM 元素或子组件实例的直接引用。这就是我们所说的“模板引用”。

核心概念

  • 目的:让你在父组件中能够直接访问并操作特定的 DOM 元素或子组件实例。
  • 场景
    • 手动聚焦一个输入框。
    • 触发一个 DOM 元素上的动画。
    • 调用子组件暴露的特定方法(非响应式数据)。
    • 测量 DOM 元素的尺寸。
    • 与需要直接 DOM 访问的第三方库集成。

基本用法

  1. 在模板中声明 ref

    在你想要引用的元素或组件上,使用 ref attribute(在 Vue 3 的 <script setup> 中,这实际上是一个特殊的指令,但用法像 attribute)。

    <template><!-- 引用一个 DOM 元素 --><input ref="inputRef" type="text" placeholder="请输入..." /><!-- 引用一个子组件 --><ChildComponent ref="childRef" />
    </template>
  2. <script setup> 中定义响应式引用

    使用 ref 函数在 <script setup> 中声明一个变量,这个变量的名字必须与模板中 ref attribute 的值完全一致。Vue 会自动将 DOM 元素或组件实例赋值给这个响应式引用。

    <script setup>
    import { ref, onMounted } from 'vue'
    import ChildComponent from './ChildComponent.vue'// 定义响应式引用,名字必须与模板中的 ref 值匹配
    const inputRef = ref(null)
    const childRef = ref(null)// 组件挂载后,引用才可用
    onMounted(() => {// 访问 DOM 元素if (inputRef.value) {inputRef.value.focus() // 让输入框自动获得焦点console.log('Input width:', inputRef.value.offsetWidth)}// 调用子组件的方法 (假设子组件暴露了 doSomething 方法)if (childRef.value) {childRef.value.doSomething()}
    })
    </script>

关键要点与注意事项

  1. 响应式引用 (ref) vs 模板引用 (ref attribute)

    • ref() 是一个函数,用于创建一个响应式引用对象。这个对象有一个 .value 属性,用来存储值(在这里是 DOM 元素或组件实例)。
    • 模板中的 ref="xxx" 是一个特殊的 attribute,它告诉 Vue 将这个元素/组件的引用注入到名字为 xxx 的响应式引用 (ref) 中。
    • 名字必须匹配const xxx = ref(null) 和 ref="xxx" 中的 xxx 必须完全相同。
  2. 初始值与访问时机

    • 通常将响应式引用初始化为 null (const myRef = ref(null)),因为在组件挂载前,DOM 元素或子组件实例还不存在。
    • 在 onMounted 生命周期钩子之前,引用的 .value 通常是 null。因为 DOM 渲染发生在 onMounted 之后。
    • 最佳实践:在 onMounted 或 onUpdated 钩子中访问引用,或者在事件处理函数中(确保元素已渲染)。
  3. 引用类型

    • DOM 元素:引用 .value 直接指向原生的 DOM 元素对象(如 HTMLInputElementHTMLDivElement 等),你可以调用其所有原生方法和属性。
    • 子组件:引用 .value 指向子组件的实例。你可以访问子组件的公开属性和方法(即在 setup 返回或在 <script setup> 中用 defineExpose 暴露的属性/方法)。
    <!-- ChildComponent.vue -->
    <script setup>
    import { ref } from 'vue'const count = ref(0)// 暴露给父组件的方法
    function increment() {count.value++
    }// 明确暴露哪些属性/方法给父组件
    defineExpose({increment,// count // 也可以暴露响应式数据,但需谨慎
    })
    </script>
  4. 访问子组件的 $el

    • 在 Vue 3 的 Composition API 中,子组件实例本身不直接是 DOM 元素。如果你需要访问子组件的根 DOM 元素,可以通过子组件实例的 $.vnode.el 属性(这是 Vue 内部的,不推荐直接依赖)或者让子组件通过 defineExpose 暴露其根元素的引用。
  5. v-for 中的模板引用

    • 当 ref 用在 v-for 内部的元素或组件上时,对应的引用将是一个包含相应数据的数组,顺序与 v-for 渲染的顺序一致。
    • 重要ref 不会随着 v-for 数据的更新而自动同步更新数组。如果数据列表变化(增删改),你需要手动管理这个引用数组,或者考虑使用其他模式(如 key + 计算属性)。
    <template><div v-for="(item, index) in list" :key="item.id" :ref="el => divs[index] = el">{{ item.text }}</div>
    </template><script setup>
    import { ref, reactive, onBeforeUpdate } from 'vue'const list = ref([{ id: 1, text: 'A' }, { id: 2, text: 'B' }])// 使用函数式 ref 回调来更灵活地收集引用
    const divs = ref([])// 在每次更新前重置引用数组,避免残留
    onBeforeUpdate(() => {divs.value = []
    })
    </script>
  6. 函数式 ref

    • 除了字符串 ref="xxx",你还可以传递一个函数 :ref="callback"。这个函数会在每次组件更新时被调用,接收 DOM 元素或组件实例作为参数。这在需要更精细控制引用收集逻辑时非常有用(如上面 v-for 的例子)。
    <template><input :ref="(el) => inputElement = el" />
    </template><script setup>
    import { ref } from 'vue'const inputElement = ref(null) // 函数内部会设置它// 或者更复杂的逻辑
    const setupInputRef = (el) => {if (el) {// 元素被挂载inputElement.value = el// 可以在这里做初始化操作} else {// 元素被卸载inputElement.value = null}
    }
    </script>
  7. TypeScript 支持

    • 在 TypeScript 中,你可以为模板引用提供精确的类型。
    <script setup lang="ts">
    import { ref, onMounted } from 'vue'
    import ChildComponent from './ChildComponent.vue'// 为 DOM 元素引用提供类型
    const inputRef = ref<HTMLInputElement | null>(null)// 为子组件引用提供类型 (需要导入组件类型)
    const childRef = ref<InstanceType<typeof ChildComponent> | null>(null)onMounted(() => {if (inputRef.value) {inputRef.value.focus() // TypeScript 知道这是 HTMLInputElement}if (childRef.value) {childRef.value.increment() // TypeScript 知道可以调用 increment}
    })
    </script>

Vue 3 的模板引用 (ref) 是一个强大且常用的特性,用于在父组件中直接操作 DOM 或子组件。核心是:

  1. 在模板中使用 ref="myName"
  2. 在 <script setup> 中使用 const myName = ref(null) 定义响应式引用。
  3. 在 onMounted 或之后访问 myName.value 来获取 DOM 元素或组件实例。
  4. 对于子组件,使用 defineExpose 来控制暴露的 API。
  5. 在 v-for 中使用时,引用是数组,需注意更新时机。
  6. 在 TypeScript 中提供精确类型。

原则:尽量通过响应式数据和 props/events 来进行组件通信,仅在确实需要直接 DOM 操作或调用特定方法时才使用模板引用。

http://www.xdnf.cn/news/1311571.html

相关文章:

  • CSS复习
  • Jenkins安装部署(Win11)和常见配置镜像加速
  • SysTick寄存器(嘀嗒定时器实现延时)
  • 要导入StandardScaler类进行数据标准化,请使用以下语句:
  • VS Code配置MinGW64编译ALGLIB库
  • 《C语言程序设计》笔记p10
  • 【数据分享】上市公司供应链成本分摊数据(2007-2024)
  • 【数据结构】-2- 泛型
  • leetcodehot100 矩阵置零
  • 基于Spring Boot 4s店车辆管理系统 租车管理系统 停车位管理系统 智慧车辆管理系统
  • 谷歌手机刷机和面具ROOT保姆级别教程
  • 利用 Java 爬虫按图搜索淘宝商品(拍立淘)实战指南
  • 《解耦的艺术:Python 观察者模式在 GUI 与事件驱动中的实战》
  • cPanel Python 应用部署流程
  • 【自动化运维神器Ansible】Ansible逻辑运算符详解:构建复杂条件判断的核心工具
  • Scala面试题及详细答案100道(11-20)-- 函数式编程基础
  • PCIE EP 框架
  • C#单元测试(xUnit + Moq + coverlet.collector)
  • RK3568 NPU RKNN(四):RKNN-ToolKit2性能和内存评估
  • springboot集成websocket
  • SpringBoot 集成Ollama 本地大模型
  • RH134 访问网络附加存储知识点
  • 【图论】分层图 / 拆点
  • 计算机存储器分类和层次结构详解
  • PyTorch生成式人工智能——使用MusicGen生成音乐
  • 探索粒子世界:从基础理论到前沿应用与未来展望
  • Python-深度学习(一)
  • flash通信
  • 机器学习核心概念精要:从定义到评估
  • C++STL标准模板库详解