前端八股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 数组。"

相关推荐
ITmaster073116 小时前
前端 AI 面试题:高频考点与实战解析
前端·人工智能
赵大仁16 小时前
Structured Output 落地:JSON Schema、重试与前端校验
前端·ai·大模型·工程化·json schema
李剑一16 小时前
有点干,前端架构基础之:Web Worker到底是什么?它和Java中的线程是一个道理吗?
前端·面试·架构
爱勇宝17 小时前
《道德经》第 10 章:真正成熟的人,能成事但不控制一切
前端·后端·程序员
看谷秀17 小时前
arkts- 8 三方库介绍
前端·arkts
六边形66617 小时前
独立开发不知道做什么?使用 TRAE Work 抓取差评痛点,快速跑通产品立项流
前端·后端·面试
亿元程序员17 小时前
小伙伴发我一个 1G 的 Cocos 项目,assets 只有几十兆
前端
paopaokaka_luck17 小时前
基于springboot3+vue3的文山民族文化资源展示与推广平台(协同过滤算法、Echarts 图形化分析)
java·前端·spring boot·学习·echarts
临江仙45518 小时前
同一个 AI Agent 如何同时服务 Web、微信和 QQ:PureChat 的渠道架构实践
前端·人工智能·后端
程序员鱼皮18 小时前
16 个超火的 DeepSeek Harness 插件,大肥鱼已经落后 N 个版本了。。。
前端·后端·ai编程