Vue.js $refs 踩坑合集:DOM 抓不到、undefined、时机错?一次说透!
·
在 Vue 项目里,想用 $refs 或 ref 直接操作 DOM 时,最常见的报错三连:
Cannot read property 'style' of undefined
this.$refs.xxx is undefined
<template ref> 拿不到
本文把 6 个高频翻车场景整理成「病历卡」,附「可复制粘贴」的急救代码,3 分钟完成定位 + 修复。
一、$refs 拿不到的根本原因
| 版本/语法 | 能拿到 DOM 的时机 | 常见坑 |
|---|---|---|
| Vue2 选项式 | mounted 之后 | 在 created 就访问 |
Vue3 <script setup> | onMounted 之后 | 用字符串模板 this.$refs |
| v-if / v-for 动态渲染 | DOM 真正插入后 | 没等 nextTick |
| 子组件 ref | 子组件实例渲染后 | 没 defineExpose |
二、6 个翻车现场 & 修复
1️⃣ 在 created 里访问 → 直接 undefined
<script>
export default {
created() {
console.log(this.$refs.box); // ❌ undefined
}
}
</script>
<template>
<div ref="box">Hello</div>
</template>
修复:改到 mounted 或加 nextTick
mounted() {
this.$nextTick(() => console.log(this.$refs.box)); // ✅
}
2️⃣ <script setup> 仍用 this.$refs
<script setup>
console.log(this.$refs.box); // ❌ 不存在 this
</script>
修复:使用组合式 ref
<script setup>
import { ref, onMounted } from 'vue'
const box = ref(null)
onMounted(() => console.log(box.value)) // ✅
</script>
<template>
<div ref="box">Hello</div>
</template>
3️⃣ v-if 切换后未等待 DOM
<template>
<button @click="show = !show">Toggle</button>
<div v-if="show" ref="panel">Panel</div>
</template>
<script setup>
import { ref, nextTick } from 'vue'
const show = ref(false)
const panel = ref(null)
async function toggle() {
show.value = false
await nextTick() // ✅ 等待 DOM 更新
console.log(panel.value) // null(符合预期)
}
</script>
4️⃣ v-for 里想拿 DOM 数组
<template>
<ul>
<li v-for="(item, i) in list" :key="i" :ref="setItemRef">{{ item }}</li>
</ul>
</template>
<script setup>
import { ref, onMounted, nextTick } from 'vue'
const list = ref(['A', 'B', 'C'])
const itemRefs = ref([])
const setItemRef = (el) => {
if (el) itemRefs.value.push(el)
}
onMounted(async () => {
await nextTick()
console.log(itemRefs.value) // ✅ [li, li, li]
})
</script>
5️⃣ 子组件实例未暴露方法
<!-- Parent.vue -->
<template>
<Child ref="child" />
<button @click="child?.focus()">Focus</button>
</template>
<script setup>
import { ref } from 'vue'
import Child from './Child.vue'
const child = ref(null)
</script>
<!-- Child.vue -->
<script setup>
import { ref } from 'vue'
const input = ref(null)
function focus() {
input.value?.focus()
}
defineExpose({ focus }) // ✅ 暴露给父组件
</script>
6️⃣ 动态组件 + ref 指向实例
<template>
<component :is="comp" ref="dynRef" />
</template>
<script setup>
import { ref, shallowRef, onMounted } from 'vue'
import CompA from './CompA.vue'
import CompB from './CompB.vue'
const comp = shallowRef(CompA)
const dynRef = ref(null)
onMounted(() => {
console.log(dynRef.value); // ✅ 当前渲染的组件实例
})
</script>
三、排查 checklist(建议打印)
| 现象 | 快速检查 |
|---|---|
undefined | 是否早于 mounted / onMounted |
<script setup> | 是否用了 this.$refs |
v-if 后拿不到 | 是否 await nextTick() |
| 子组件方法 | 是否 defineExpose |
| 路径/语法 | Vue2 vs Vue3 语法混用 |
四、一句话总结
“拿不到 DOM” 90% 是时机错,9% 是语法混,1% 才是玄学。
牢记:
- Vue2 等
mounted- Vue3
<script setup>用ref+onMounted- 动态渲染后
nextTick
照此清单操作,$refs 永不翻车。
最后问候亲爱的朋友们,并邀请你们阅读我的全新著作

更多推荐
所有评论(0)