一、Vue 中获取 DOM 节点的方法:从基础到实战
1.1 Vue 数据驱动机制与 DOM 操作的必要性
Vue 是一个流行的 JavaScript 框架,其核心思想是数据驱动视图,即数据的变化会自动反映到 DOM 上。然而,在实际开发中,我们经常需要直接操作 DOM 元素,例如调用第三方库的 API 或计算复杂的滚动位置。因此,掌握如何获取 DOM 节点对于开发者来说至关重要。
1.2 获取 DOM 的核心目标
我们获取 DOM 节点通常是为了获取其属性、内容或将其作为事件目标。Vue 推荐尽量避免直接操作 DOM,只有在确实需要时才进行操作,以减少对框架内部状态的破坏。
2.1 ref 的基本使用
在 Vue 2 中,最常用的获取 DOM 节点的方法是使用 ref 属性。在模板元素上绑定 ref,可以在组件实例中通过 this.$refs 访问该 DOM 元素。
<template>
<div id="app">
<div ref="myBox">我是 DOM 节点</div>
<button @click="handleClick">获取节点</button>
</div>
</template>
<script>
export default {
methods: {
handleClick() {
const domNode = this.$refs.myBox;
console.log(domNode.innerText);
}
}
}
</script>
2.2 ref 在 v-for 循环中的应用
当 ref 用于列表渲染时,this.$refs.refName 会是一个数组,包含所有匹配该 ref 值的元素。
<div ref="container" v-for="(item, index) in list" :key="index">
{{ item }}
</div>
要获取特定的某个 DOM 节点,可以直接通过数组的下标访问:this.$refs.container[index]。
2.3 组件 ref
除了 DOM 元素,ref 还可以用于获取子组件的实例。这允许父组件访问子组件的属性和方法。
<child-component ref="child"></child-component>
访问时:this.$refs.child.someMethod()。
3.1 Vue 3 的 DOMRef
在 Vue 3 中,特别是在使用 Composition API 和 <script setup> 语法时,获取 DOM 的方式发生了变化。ref 变量可以直接在模板中绑定,无需通过 this.$refs。
<script setup>
import { ref, onMounted } from 'vue';
const myBox = ref(null);
onMounted(() => {
if (myBox.value) {
console.log(myBox.value.innerText);
}
})
</script>
<template>
<div ref="myBox">Vue 3 获取节点</div>
</template>
3.2 $nextTick 的作用
Vue 的 DOM 更新是异步的。当你修改数据时,Vue 不会立即重新渲染页面,而是把数据放在一个队列中,等同一个“事件循环”中的所有数据变化完成后再统一渲染。
如果在数据修改后立即通过 ref 获取 DOM,可能会获取到旧的 DOM 状态。这时需要使用 $nextTick 方法,等待 DOM 更新完成后再执行操作。
methods: {
updateData() {
this.message = '新内容';
// 此时获取 DOM 可能是旧的
console.log(this.$refs.myBox.innerText);
this.$nextTick(() => {
// 这里 DOM 已经更新,可以安全操作
console.log(this.$refs.myBox.innerText); // 输出 '新内容'
});
}
}
4.1 DOM 操作流程解析
Vue 中的 DOM 操作通常涉及数据的变更、DOM 的更新以及最终的获取与操作。为了更清晰地展示这一过程,我们可以参考以下流程图。
4.2 完整流程图
#publish-mermaid-1785343656364-0{font-family:inherit;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#publish-mermaid-1785343656364-0 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#publish-mermaid-1785343656364-0 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#publish-mermaid-1785343656364-0 .error-icon{fill:#552222;}#publish-mermaid-1785343656364-0 .error-text{fill:#552222;stroke:#552222;}#publish-mermaid-1785343656364-0 .edge-thickness-normal{stroke-width:1px;}#publish-mermaid-1785343656364-0 .edge-thickness-thick{stroke-width:3.5px;}#publish-mermaid-1785343656364-0 .edge-pattern-solid{stroke-dasharray:0;}#publish-mermaid-1785343656364-0 .edge-thickness-invisible{stroke-width:0;fill:none;}#publish-mermaid-1785343656364-0 .edge-pattern-dashed{stroke-dasharray:3;}#publish-mermaid-1785343656364-0 .edge-pattern-dotted{stroke-dasharray:2;}#publish-mermaid-1785343656364-0 .marker{fill:#333333;stroke:#333333;}#publish-mermaid-1785343656364-0 .marker.cross{stroke:#333333;}#publish-mermaid-1785343656364-0 svg{font-family:inherit;font-size:16px;}#publish-mermaid-1785343656364-0 p{margin:0;}#publish-mermaid-1785343656364-0 .label{font-family:inherit;color:#333;}#publish-mermaid-1785343656364-0 .cluster-label text{fill:#333;}#publish-mermaid-1785343656364-0 .cluster-label span{color:#333;}#publish-mermaid-1785343656364-0 .cluster-label span p{background-color:transparent;}#publish-mermaid-1785343656364-0 .label text,#publish-mermaid-1785343656364-0 span{fill:#333;color:#333;}#publish-mermaid-1785343656364-0 .node rect,#publish-mermaid-1785343656364-0 .node circle,#publish-mermaid-1785343656364-0 .node ellipse,#publish-mermaid-1785343656364-0 .node polygon,#publish-mermaid-1785343656364-0 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#publish-mermaid-1785343656364-0 .rough-node .label text,#publish-mermaid-1785343656364-0 .node .label text,#publish-mermaid-1785343656364-0 .image-shape .label,#publish-mermaid-1785343656364-0 .icon-shape .label{text-anchor:middle;}#publish-mermaid-1785343656364-0 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#publish-mermaid-1785343656364-0 .rough-node .label,#publish-mermaid-1785343656364-0 .node .label,#publish-mermaid-1785343656364-0 .image-shape .label,#publish-mermaid-1785343656364-0 .icon-shape .label{text-align:center;}#publish-mermaid-1785343656364-0 .node.clickable{cursor:pointer;}#publish-mermaid-1785343656364-0 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#publish-mermaid-1785343656364-0 .arrowheadPath{fill:#333333;}#publish-mermaid-1785343656364-0 .edgePath .path{stroke:#333333;stroke-width:1px;}#publish-mermaid-1785343656364-0 .flowchart-link{stroke:#333333;fill:none;}#publish-mermaid-1785343656364-0 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#publish-mermaid-1785343656364-0 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#publish-mermaid-1785343656364-0 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#publish-mermaid-1785343656364-0 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#publish-mermaid-1785343656364-0 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#publish-mermaid-1785343656364-0 .cluster text{fill:#333;}#publish-mermaid-1785343656364-0 .cluster span{color:#333;}#publish-mermaid-1785343656364-0 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:inherit;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#publish-mermaid-1785343656364-0 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#publish-mermaid-1785343656364-0 rect.text{fill:none;stroke-width:0;}#publish-mermaid-1785343656364-0 .icon-shape,#publish-mermaid-1785343656364-0 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#publish-mermaid-1785343656364-0 .icon-shape p,#publish-mermaid-1785343656364-0 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#publish-mermaid-1785343656364-0 .icon-shape .label rect,#publish-mermaid-1785343656364-0 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#publish-mermaid-1785343656364-0 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#publish-mermaid-1785343656364-0 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#publish-mermaid-1785343656364-0 .node .neo-node{stroke:#9370DB;}#publish-mermaid-1785343656364-0 [data-look=\”neo\”].node rect,#publish-mermaid-1785343656364-0 [data-look=\”neo\”].cluster rect,#publish-mermaid-1785343656364-0 [data-look=\”neo\”].node polygon{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785343656364-0 [data-look=\”neo\”].swimlane.cluster rect{filter:none;}#publish-mermaid-1785343656364-0 [data-look=\”neo\”].node path{stroke:#9370DB;stroke-width:1px;}#publish-mermaid-1785343656364-0 [data-look=\”neo\”].node .outer-path{filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785343656364-0 [data-look=\”neo\”].node .neo-line path{stroke:#9370DB;filter:none;}#publish-mermaid-1785343656364-0 [data-look=\”neo\”].node circle{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785343656364-0 [data-look=\”neo\”].node circle .state-start{fill:#000000;}#publish-mermaid-1785343656364-0 [data-look=\”neo\”].icon-shape .icon{fill:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785343656364-0 [data-look=\”neo\”].icon-shape .icon-neo path{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785343656364-0 :root{–mermaid-font-family:inherit;}
否
是
开始操作 DOM
选择获取方式
ref / DOMRef
$nextTick 机制
获取 DOM 引用
修改数据触发重绘
检查 DOM 是否更新
执行 Vue 更新队列
执行回调函数
完成 DOM 操作
结束
5.1 最佳实践总结
在使用 Vue 获取 DOM 节点时,应遵循以下原则:
5.2 结语
获取 DOM 节点是 Vue 开发中的基础技能,理解 ref 和 $nextTick 的机制能够帮助开发者编写出更健壮的代码。希望本文的详解能对你的前端开发有所帮助。

