vue3组件的ref访问与defineExpose暴露机制
vue官方文档:
ref :cn.vuejs.org/api/reactiv...
defineExpose :cn.vuejs.org/api/sfc-scr...
以el-button举例:
1. 正确的访问方式
看 Button 组件暴露的内容:
83:94:packages/components/button/src/button.vue
defineExpose({
/** @description button html element */
ref: _ref,
/** @description button size */
size: _size,
/** @description button type */
type: _type,
/** @description button disabled */
disabled: _disabled,
/** @description whether adding space */
shouldAddSpace,
})
2. 实际使用示例
vue
<template>
<el-button ref="buttonRef" type="primary" size="large">
按钮
</el-button>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import type { ButtonInstance } from 'element-plus'
const buttonRef = ref<ButtonInstance>()
onMounted(() => {
// ✅ 正确:访问所有暴露的属性
console.log('DOM 元素:', buttonRef.value?.ref) // HTMLButtonElement
console.log('按钮尺寸:', buttonRef.value?.size) // ComputedRef<'large'>
console.log('按钮类型:', buttonRef.value?.type) // ComputedRef<'primary'>
console.log('是否禁用:', buttonRef.value?.disabled) // ComputedRef<boolean>
console.log('是否加空格:', buttonRef.value?.shouldAddSpace) // ComputedRef<boolean>
// ✅ 打印整个组件实例,可以看到所有暴露的属性
console.log('组件实例:', buttonRef.value)
})
</script>
3. 打印结果示例
当你 console.log(buttonRef.value) 时,会看到类似:
js
{
ref: HTMLButtonElement, // DOM 元素
size: ComputedRef<'large'>, // 尺寸(注意是 ComputedRef)
type: ComputedRef<'primary'>, // 类型(注意是 ComputedRef)
disabled: ComputedRef<false>, // 禁用状态(注意是 ComputedRef)
shouldAddSpace: ComputedRef<false> // 是否加空格(注意是 ComputedRef)
}
4. 重要提示:ComputedRef 的访问
注意 size、type、disabled 等是 ComputedRef,访问值需要用 .value:
ts
// ❌ 错误:这样得到的是 ComputedRef 对象
console.log(buttonRef.value?.size) // ComputedRef { ... }
// ✅ 正确:需要 .value 才能拿到实际值
console.log(buttonRef.value?.size.value) // 'large'
console.log(buttonRef.value?.type.value) // 'primary'
console.log(buttonRef.value?.disabled.value) // false
说明 Vue 3 的生命周期和 ref 访问时机:
1. Vue 3 没有 onCreated 钩子
在 Vue 3 的 Composition API 中:
- 没有
onCreated()钩子 setup()函数本身就相当于 Vue 2 的created+beforeCreate- 如果需要访问 DOM 或组件实例,应该用
onMounted()
2. 为什么必须在 onMounted() 中?
在 setup() 顶层(组件未挂载)
vue
<script setup>
import { ref } from 'vue'
import type { ButtonInstance } from 'element-plus'
const buttonRef = ref<ButtonInstance>()
// ❌ 错误:此时 buttonRef.value 是 undefined
// 因为组件还没有挂载,ref 还没有被赋值
console.log(buttonRef.value) // undefined
</script>
在 onMounted() 中(组件已挂载)
vue
<script setup>
import { ref, onMounted } from 'vue'
import type { ButtonInstance } from 'element-plus'
const buttonRef = ref<ButtonInstance>()
onMounted(() => {
// ✅ 正确:此时组件已经挂载,ref 已经被赋值
console.log(buttonRef.value) // ButtonInstance 对象
console.log(buttonRef.value?.ref) // HTMLButtonElement
})
</script>
3. Vue 3 生命周期对比
| Vue 2 Options API | Vue 3 Composition API | 说明 |
|---|---|---|
beforeCreate |
setup() 开始执行 |
组件创建前 |
created |
setup() 执行中 |
组件创建后(但未挂载) |
beforeMount |
onBeforeMount() |
挂载前 |
mounted |
onMounted() |
挂载后(DOM 已存在) |
beforeUpdate |
onBeforeUpdate() |
更新前 |
updated |
onUpdated() |
更新后 |
beforeUnmount |
onBeforeUnmount() |
卸载前 |
unmounted |
onUnmounted() |
卸载后 |
4. 完整示例对比
错误示例(在 setup 顶层)
vue
<template>
<el-button ref="buttonRef">按钮</el-button>
</template>
<script setup>
import { ref } from 'vue'
import type { ButtonInstance } from 'element-plus'
const buttonRef = ref<ButtonInstance>()
// ❌ 错误:此时 buttonRef.value 是 undefined
console.log('setup 顶层:', buttonRef.value) // undefined
</script>
正确示例(在 onMounted 中)
vue
<template>
<el-button ref="buttonRef">按钮</el-button>
</template>
<script setup>
import { ref, onMounted, onBeforeMount } from 'vue'
import type { ButtonInstance } from 'element-plus'
const buttonRef = ref<ButtonInstance>()
// 在 setup 顶层
console.log('setup 顶层:', buttonRef.value) // undefined
// 在 beforeMount 中
onBeforeMount(() => {
console.log('beforeMount:', buttonRef.value) // 可能还是 undefined
})
// 在 mounted 中
onMounted(() => {
// ✅ 正确:此时组件已挂载,ref 已赋值
console.log('mounted:', buttonRef.value) // ButtonInstance 对象
console.log('DOM 元素:', buttonRef.value?.ref) // HTMLButtonElement
})
</script>
5. 为什么 ref 在 onMounted 中才有值?
Vue 的 ref 赋值时机:
- 模板编译阶段:Vue 识别
ref="buttonRef" - 组件挂载阶段:创建组件实例,将实例赋值给
buttonRef.value - DOM 渲染完成:
onMounted()执行时,ref 已经有值
6. 如果需要在 setup 中访问怎么办?
可以使用 watchEffect 或 watch:
vue
<script setup>
import { ref, watchEffect } from 'vue'
import type { ButtonInstance } from 'element-plus'
const buttonRef = ref<ButtonInstance>()
// 使用 watchEffect,会在 ref 有值后自动执行
watchEffect(() => {
if (buttonRef.value) {
console.log('ref 有值了:', buttonRef.value)
}
})
</script>
7. 总结
- Vue 3 没有
onCreated(),setup()本身就相当于created - 访问
ref.value必须在onMounted()中,因为此时组件已挂载 - 在
setup()顶层访问ref.value会是undefined - 如果需要响应式监听 ref 的变化,可以用
watchEffect或watch