前端八股Vue---ref操作 DOM 元素或组件,调用子组件方法

目录

[一、ref 的核心原理](#一、ref 的核心原理)

[二、获取 DOM 元素](#二、获取 DOM 元素)

基础用法

[常用 DOM 操作](#常用 DOM 操作)

三、获取组件实例(调用子组件方法)

[子组件:必须用 defineExpose 暴露](#子组件:必须用 defineExpose 暴露)

[父组件:通过 ref 调用](#父组件:通过 ref 调用)

[四、ref 的生命周期](#四、ref 的生命周期)

[五、v-for 中的 ref](#五、v-for 中的 ref)

六、常用场景

场景1:输入框自动聚焦

场景2:获取元素尺寸

场景3:父组件重置子组件表单

[七、Vue 2 vs Vue 3 对比](#七、Vue 2 vs Vue 3 对比)

八、面试回答


ref 是 Vue 提供的模板引用机制 ,通过在模板中给元素或组件添加 ref 属性,在 JS 中创建同名 ref 变量,Vue 会在组件挂载后自动将 DOM 元素或组件实例赋值给 ref.value,从而直接操作 DOM 或调用子组件方法。


一、ref 的核心原理

javascript 复制代码
<template>
  <!-- 2. 模板中写 ref="xxx" -->
  <input ref="inputRef" />
</template>

<script setup>
import { ref, onMounted } from 'vue'

// 1. JS 中创建同名 ref,初始值为 null
const inputRef = ref(null)

// 3. 组件挂载后,Vue 自动赋值:inputRef.value = DOM 元素
onMounted(() => {
  inputRef.value.focus()  // 可以直接操作 DOM
})
</script>

流程

复制代码
1. 创建 ref(null)
2. 模板渲染,发现 ref="inputRef"
3. 组件挂载完成
4. Vue 自动执行:inputRef.value = 这个 DOM 元素
5. 现在可以操作 DOM 了

二、获取 DOM 元素

基础用法

javascript 复制代码
<template>
  <div>
    <input ref="inputRef" type="text" />
    <button @click="focusInput">聚焦输入框</button>
    <div ref="divRef" class="box">这是一个盒子</div>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'

const inputRef = ref(null)
const divRef = ref(null)

// 组件挂载后可以访问
onMounted(() => {
  console.log(inputRef.value)  // <input type="text">
  console.log(divRef.value)    // <div class="box">这是一个盒子</div>
  console.log(divRef.value.offsetHeight)  // 获取高度
})

const focusInput = () => {
  inputRef.value?.focus()  // 聚焦输入框
}
</script>

常用 DOM 操作

javascript 复制代码
const divRef = ref(null)

// 获取元素尺寸
const getSize = () => {
  const el = divRef.value
  console.log('宽度:', el.offsetWidth)
  console.log('高度:', el.offsetHeight)
  console.log('位置:', el.getBoundingClientRect())
}

// 滚动到元素
const scrollToElement = () => {
  divRef.value?.scrollIntoView({ behavior: 'smooth' })
}

// 操作样式
const changeStyle = () => {
  divRef.value.style.backgroundColor = 'red'
}

三、获取组件实例(调用子组件方法)

子组件:必须用 defineExpose 暴露

javascript 复制代码
<!-- Child.vue -->
<script setup>
import { ref } from 'vue'

const count = ref(0)

// 定义方法
const increment = () => {
  count.value++
}

const reset = () => {
  count.value = 0
}

const getCount = () => {
  return count.value
}

// 👇 关键:暴露给父组件
defineExpose({
  increment,
  reset,
  getCount,
  count
})
</script>

<template>
  <div>
    <p>子组件计数:{{ count }}</p>
  </div>
</template>

父组件:通过 ref 调用

javascript 复制代码
<!-- Parent.vue -->
<template>
  <div>
    <Child ref="childRef" />
    <button @click="handleIncrement">调用子组件 increment</button>
    <button @click="handleReset">调用子组件 reset</button>
    <button @click="handleGetCount">获取子组件 count</button>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import Child from './Child.vue'

const childRef = ref(null)

const handleIncrement = () => {
  childRef.value?.increment()  // 调用子组件方法
}

const handleReset = () => {
  childRef.value?.reset()
}

const handleGetCount = () => {
  alert(`当前计数:${childRef.value?.getCount()}`)
}
</script>

四、ref 的生命周期

阶段 ref.value 的值
setup() null(还没挂载)
onBeforeMount null
onMounted DOM 元素或组件实例
onBeforeUpdate 更新前的值
onUpdated 更新后的值
onBeforeUnmount 还有值
onUnmounted null(组件已销毁)
javascript 复制代码
<script setup>
import { ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted } from 'vue'

const inputRef = ref(null)

console.log('setup:', inputRef.value)  // null

onBeforeMount(() => {
  console.log('onBeforeMount:', inputRef.value)  // null
})

onMounted(() => {
  console.log('onMounted:', inputRef.value)  // <input> 元素
})

onBeforeUpdate(() => {
  console.log('onBeforeUpdate:', inputRef.value)  // 仍然有值
})

onBeforeUnmount(() => {
  console.log('onBeforeUnmount:', inputRef.value)  // 仍然有值
})

onUnmounted(() => {
  console.log('onUnmounted:', inputRef.value)  // null
})
</script>

五、v-for 中的 ref

javascript 复制代码
<template>
  <div>
    <!-- v-for 中的 ref 需要用函数形式收集 -->
    <div 
      v-for="item in list" 
      :key="item.id"
      :ref="(el) => setItemRef(el, item.id)"
    >
      {{ item.name }}
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const list = ref([
  { id: 1, name: 'A' },
  { id: 2, name: 'B' },
  { id: 3, name: 'C' }
])

const itemRefs = ref([])

const setItemRef = (el, id) => {
  if (el) {
    itemRefs.value.push({ id, el })
  }
}

// 或者用 Map 存储
const itemMap = ref(new Map())
const setItemMap = (el, id) => {
  if (el) {
    itemMap.value.set(id, el)
  }
}
</script>

六、常用场景

场景1:输入框自动聚焦

javascript 复制代码
<template>
  <input ref="inputRef" v-if="showInput" />
  <button @click="showInput = true">显示输入框</button>
</template>

<script setup>
import { ref, watch, nextTick } from 'vue'

const showInput = ref(false)
const inputRef = ref(null)

watch(showInput, async (newVal) => {
  if (newVal) {
    await nextTick()  // 等待 DOM 更新
    inputRef.value?.focus()
  }
})
</script>

场景2:获取元素尺寸

javascript 复制代码
<template>
  <div ref="boxRef" class="box">内容</div>
  <button @click="getSize">获取尺寸</button>
</template>

<script setup>
import { ref } from 'vue'

const boxRef = ref(null)

const getSize = () => {
  if (boxRef.value) {
    const { width, height, top, left } = boxRef.value.getBoundingClientRect()
    console.log(`宽度:${width},高度:${height}`)
  }
}
</script>

场景3:父组件重置子组件表单

javascript 复制代码
<!-- 父组件 -->
<template>
  <UserForm ref="formRef" />
  <button @click="resetForm">重置表单</button>
</template>

<script setup>
import { ref } from 'vue'
import UserForm from './UserForm.vue'

const formRef = ref(null)

const resetForm = () => {
  formRef.value?.reset()
}
</script>
javascript 复制代码
<!-- UserForm.vue 子组件 -->
<script setup>
import { reactive } from 'vue'

const formData = reactive({
  name: '',
  email: ''
})

const reset = () => {
  formData.name = ''
  formData.email = ''
}

defineExpose({ reset })
</script>

七、Vue 2 vs Vue 3 对比

在 Vue 3 中,ref 是操作 DOM 元素和调用子组件方法的唯一官方推荐方式 。Vue 2 中的 $refs 仍然存在,但组合式 API 中统一使用 ref

操作 Vue 2(选项式 API) Vue 3(组合式 API)
获取 DOM this.$refs.xxx const xxxRef = ref(null)
调用子组件 this.$refs.child.method() childRef.value.method()
模板中写法 ref="xxx" ref="xxxRef"

八、面试回答

"ref 是 Vue 获取 DOM 元素或组件实例的方式。

用法

  1. 在 JS 中创建 const inputRef = ref(null)

  2. 在模板中写 ref="inputRef"

  3. 组件挂载后,inputRef.value 就是 DOM 元素或组件实例

获取 DOM :直接操作元素,如 inputRef.value.focus()

获取组件实例 :子组件需要用 defineExpose 暴露方法,父组件通过 childRef.value.method() 调用

生命周期setup 中是 nullonMounted 后才能拿到

注意 :ref 名称必须匹配,调用时用可选链 ?. 防止报错,v-for 中需要用函数形式收集 ref 数组。"

相关推荐
NiceCloud喜云1 天前
Opus 4.8 的 Effort Control 怎么选:Low 到 Max 五档策略
android·java·大数据·前端·c++·python·spring
wordbaby1 天前
React Native + RNOH:跨页面数据回传的最佳实践与避坑指南
前端·react native
GISer_Jing1 天前
Three.js着色器编译机制深度解析
javascript·webgl·着色器
丷丩1 天前
MapLibre GL JS第22课:查看本地GeoJSON
前端·javascript·map·mapbox·maplibre gl js
油炸自行车1 天前
Claude Code 错误:API Error: 400 Failed to deserialize the JSON body into the
开发语言·javascript·json·trae·claude code·api error 400
Front思1 天前
AI前端工程师需要具备能力+
前端·人工智能·ai
ZC跨境爬虫1 天前
跟着 MDN 学CSS day_29:(掌握文本与字体样式的核心艺术)
前端·css·ui·html·tensorflow
李子琪。1 天前
网络空间安全深度实战:CSRF 漏洞原理剖析与基于 Token 的纵深防御体系构建(全栈实验报告)
前端·安全·csrf
冰暮流星1 天前
javascript之history对象介绍
前端·笔记
IT_陈寒1 天前
Vite热更新失灵?你可能漏了这个配置
前端·人工智能·后端