Vue 中如何获取 DOM 元素?除了 `$refs` 还有其他方式吗?

在 Vue 开发中,虽然我们推崇“数据驱动视图”的理念,尽量避免直接操作 DOM,但在某些特定场景下(如集成第三方库、聚焦输入框、获取元素尺寸等),我们仍然需要直接访问 DOM 元素。
大多数开发者第一时间想到的就是 $refs。确实,它是 Vue 提供的官方标准方案。但是,你是否知道除了 $refs,还有其他几种优雅甚至更灵活的方式来获取和操作 DOM?
本文将深入探讨 Vue(涵盖 Vue 2 和 Vue 3)中获取 DOM 元素的多种方法,帮助你根据具体场景选择最佳实践。

方法一:标准的 ref 属性(最推荐)

这是 Vue 官方首推的方式,适用于绝大多数场景。它的特点是响应式安全,Vue 会确保在组件挂载完成后才填充 ref。

Vue 3 (Composition API)

在 Vue 3 的组合式 API 中,我们直接使用 ref() 函数创建一个同名变量。
vue

编辑
1<template>
2  <div ref="myBox">我是一个盒子</div>
3  <button @click="logWidth">获取宽度</button>
4</template>
5
6<script setup>
7import { ref, onMounted } from 'vue';
8
9// 1. 创建一个同名的 ref 变量
10const myBox = ref(null);
11
12const logWidth = () => {
13  if (myBox.value) {
14    console.log('宽度:', myBox.value.offsetWidth);
15  }
16};
17
18// 确保在挂载后访问,虽然 ref 在模板渲染后会自动赋值,但涉及尺寸通常建议 onMounted
19onMounted(() => {
20  console.log('DOM 已就绪:', myBox.value);
21});
22</script>

Vue 2 / Vue 3 (Options API)

在选项式 API 中,通过 this.$refs 访问。
javascript

编辑
1export default {
2  mounted() {
3    // this.$refs.myBox 即为 DOM 元素
4    console.log(this.$refs.myBox.offsetWidth);
5  }
6}
优点
  • 类型安全(配合 TypeScript 极佳)。
  • 生命周期管理清晰,组件卸载后自动清理。
  • 能够获取子组件实例(而不仅仅是 DOM)。
缺点
  • 需要在模板和脚本中维护相同的命名。
  • 在 v-for 中使用时,Vue 3 中会得到一个数组,Vue 2 中行为略有不同,处理稍显繁琐。

方法二:原生 JavaScript 选择器(document.querySelector

既然 Vue 最终渲染的是标准 HTML,那么原生的 DOM API 当然依然可用。
javascript

编辑
1import { onMounted } from 'vue';
2
3onMounted(() => {
4  const element = document.querySelector('.my-custom-class');
5  if (element) {
6    console.log('通过类名获取:', element);
7  }
8});
适用场景
  • 操作非 Vue 管理的 DOM(如由第三方库动态插入的元素)。
  • 快速调试或临时脚本。
⚠️ 风险与缺点
  • 破坏封装性:如果页面中有多个相同类名的元素,可能会选错。
  • SSR 问题:在服务端渲染(SSR)时,document 对象不存在,直接调用会报错,必须包裹在 onMounted 或 client-only 逻辑中。
  • 难以追踪:这种隐式的依赖关系让代码更难维护和重构。
建议:除非万不得已,否则不推荐在组件内部常规使用此方法。

方法三:自定义指令 (Custom Directives)

当你需要在 DOM 元素被插入页面时立即执行某些逻辑(如自动聚焦、初始化图表),自定义指令是比生命周期钩子更优雅的方案。指令的 mounted 钩子直接接收真实的 DOM 元素作为参数。
javascript

编辑
1// directives/focus.js
2export default {
3  mounted(el) {
4    // el 就是绑定的 DOM 元素,无需任何额外获取步骤
5    el.focus();
6    console.log('元素已通过指令获取:', el);
7  }
8};
vue

编辑
1<template>
2  <!-- 直接使用指令 -->
3  <input v-focus type="text" />
4</template>
5
6<script setup>
7import focus from './directives/focus';
8</script>
优点
  • 逻辑复用:将 DOM 操作逻辑封装在指令中,可在多个组件复用。
  • 时机精准:直接在元素挂载时触发,无需担心生命周期顺序。
  • 代码解耦:模板中声明意图,脚本中无需编写获取 DOM 的代码。
缺点
  • 需要额外的文件注册和管理。
  • 对于一次性简单操作,可能显得“杀鸡用牛刀”。

方法四:事件对象 ($event)

如果你获取 DOM 的目的是为了响应用户的交互(如点击、鼠标移动),那么事件处理函数中的 $event 对象直接包含了触发事件的 DOM 元素(event.target 或 event.currentTarget)。
vue

编辑
1<template>
2  <button @click="handleClick">点击我</button>
3</template>
4
5<script setup>
6const handleClick = (event) => {
7  // event.currentTarget 是绑定监听器的元素
8  const btn = event.currentTarget; 
9  console.log('按钮宽度:', btn.offsetWidth);
10  
11  // event.target 是实际触发事件的元素(如果有子元素可能是子元素)
12  console.log('触发源:', event.target);
13};
14</script>
优点
  • 完全不需要预先定义 ref。
  • 天然适配合交互相关的 DOM 操作。
  • 性能最好,无额外开销。
局限
  • 仅适用于事件触发场景,无法在组件初始化时主动获取。

方法五:通过组件实例 $el (仅限根节点)

在 Vue 中,每个组件实例都有一个 $el 属性,指向该组件的根 DOM 元素
javascript

编辑
1// 在组件内部
2onMounted(() => {
3  console.log('根元素:', this.$el); // Options API
4  // 或 getCurrentInstance().proxy.$el (Composition API 不推荐直接这样用,通常结合 ref)
5});
注意
  • 它只能获取组件模板的最外层元素。
  • 如果根元素是 <template> 或多根节点(Vue 3 Fragments),$el 的行为会有所不同(多根节点时为第一个节点)。
  • 父组件获取子组件的 DOM:父组件可以通过 ref 获取子组件实例,然后访问 childComponentRef.value.$el

方法六:v-for 中的函数式 Ref (进阶技巧)

在 Vue 3 中,:ref 可以绑定一个函数。这在处理列表动态生成的 DOM 时非常有用,可以将所有 DOM 元素收集到一个数组或对象中,而不需要手动遍历 $refs
vue

编辑
1<template>
2  <div v-for="item in list" :key="item.id" :ref="setRef">
3    {{ item.name }}
4  </div>
5</template>
6
7<script setup>
8import { ref } from 'vue';
9
10const domList = ref([]);
11
12const setRef = (el) => {
13  if (el) {
14    domList.value.push(el);
15  }
16};
17
18// 此时 domList.value 就是一个包含所有对应 DOM 元素的数组
19</script>
优点
  • 完美解决 v-for 中 ref 变成数组后难以对应索引的问题(你可以自定义存储结构,比如 Map)。
  • 灵活性极高。

总结与最佳实践对比

表格

方法 适用场景 推荐指数 备注
ref 属性 通用场景,需频繁访问或操作特定元素 ⭐⭐⭐⭐⭐ 首选方案,类型友好,维护性强
事件对象 $event 用户交互触发的 DOM 操作 ⭐⭐⭐⭐⭐ 最自然的事件处理方式
自定义指令 底层 DOM 操作逻辑复用(如聚焦、懒加载) ⭐⭐⭐⭐ 封装性好,适合通用工具
$el 仅需访问组件根节点 ⭐⭐⭐ 局限性较大,常用于父子组件通信
函数式 Ref v-for 列表中的复杂 DOM 收集 ⭐⭐⭐⭐ 解决列表引用痛点
原生 JS 选择器 第三方库集成、非 Vue 管控区域 ⭐⭐ 慎用,易导致耦合和 SSR 错误

💡 核心建议

  1. 首选 ref:在 90% 的情况下,ref 都是最清晰、最安全的选择。
  2. 避免直接操作 DOM:如果可以通过数据绑定(:class:stylev-if)解决问题,就不要去摸 DOM。
  3. 注意生命周期:无论用哪种方法,务必确保在 DOM 渲染完成后(如 onMounted)再进行读取尺寸或位置的操作。
  4. SSR 兼容性:如果你的应用涉及服务端渲染,请绝对避免在 setup 顶层或 beforeMount 中使用依赖 window/document 的方法(如原生选择器),务必放入 onMounted
掌握这些多样化的方法,能让你的 Vue 代码更加灵活、健壮,也能在面对复杂需求时游刃有余。下次需要操作 DOM 时,不妨想想除了 $refs,是否还有更适合当前场景的“利器”?

购买须知/免责声明
1.本文部分内容转载自其它媒体,但并不代表本站赞同其观点和对其真实性负责。
2.若您需要商业运营或用于其他商业活动,请您购买正版授权并合法使用。
3.如果本站有侵犯、不妥之处的资源,请在网站右边客服联系我们。将会第一时间解决!
4.本站所有内容均由互联网收集整理、网友上传,仅供大家参考、学习,不存在任何商业目的与商业用途。
5.本站提供的所有资源仅供参考学习使用,版权归原著所有,禁止下载本站资源参与商业和非法行为,请在24小时之内自行删除!
6.不保证任何源码框架的完整性。
7.侵权联系邮箱:aliyun6168@gail.com / aliyun666888@gail.com
8.若您最终确认购买,则视为您100%认同并接受以上所述全部内容。

会员源码网 建站教程 Vue 中如何获取 DOM 元素?除了 `$refs` 还有其他方式吗? https://svipm.com/21261.html

相关文章

猜你喜欢