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

相关推荐
布局呆星1 小时前
Vue3 路由守卫详解:全局守卫、路由独享守卫、组件内守卫
前端·javascript·vue.js
Yoram1 小时前
Vue3 响应性:跨上下文的传递、转换与作用域控制
前端·vue.js
掘金安东尼1 小时前
开源小工具:掘金福利页「补签卡」按次数自动兑换(Chrome 扩展)
前端·开源
Mike_jia1 小时前
Sirius Scan:开源漏洞扫描利器,重塑企业安全防护体系
前端
知兀2 小时前
【前端】默认导出和命名导出区别
前端
XS0301062 小时前
Servlet+JQuery实现数据库数据渲染到前端页面
前端·servlet·jquery
yqcoder2 小时前
深入理解 JavaScript:什么是可迭代对象 (Iterable)?
开发语言·javascript·网络
van久2 小时前
Day27:菜单管理 + 动态路由(前端可直接用!)
前端·状态模式
恋猫de小郭2 小时前
DeepSeek V4 Flash 可以在 128GB 的 M3 Max 运行,还是 1M 上下文
前端·人工智能·ai编程