vue 3 中 ref 本身不缓存状态,真正缓存弹窗显隐的是 ref 包裹的变量;需子组件用 ref 管理状态并 defineexpose 暴露 open/close 方法,父组件用模板 ref 获取实例调用,支持传参增强复用性,注意区分 dom ref 与组件 ref。

在 Vue 3 中,ref 本身不缓存状态,它只是创建一个响应式引用;真正“缓存”弹窗显隐状态的,是 ref 所包裹的变量(比如 ref(false))在组件生命周期内持续存在。关键在于:用 ref 管理状态 + 正确暴露控制逻辑 + 合理复用实例,就能实现稳定、可复用的显隐控制开关。
1. 子组件:用 ref 包裹状态,并通过 defineExpose 暴露开关方法
子组件内部用 ref 创建响应式显隐状态,并定义 open() 和 close() 方法。这些方法必须显式通过 defineExpose 暴露,父组件才能调用。
- 状态声明:
const isOpen = ref(false)—— 这个值会在组件卸载前一直保留(即“缓存”) - 方法封装:
function open() { isOpen.value = true },function close() { isOpen.value = false } - 必须暴露:
defineExpose({ open, close }),否则父组件拿到的是空 ref 实例
2. 父组件:用模板 ref 获取实例,直接调用方法控制显隐
父组件通过 ref="xxx" 绑定子组件实例,配合 ref(null) 初始化,在 setup 中即可安全调用子组件暴露的方法。
- 声明 ref:
const dialogRef = ref(null) - 触发打开:
@click="dialogRef.value?.open()"或在 JS 中调用dialogRef.value.open() - 避免空调用:加可选链
.value?.open()防止组件未挂载时报错
3. 进阶技巧:支持传参打开,让“开关”更智能
open 方法可以接收参数(如标题、初始数据、模式),让同一个弹窗组件适配新增/编辑等不同场景,状态依然由 ref 持有。
- 子组件中:
function open(options = {}) { title.value = options.title; formData.value = options.data || {}; isOpen.value = true } - 父组件调用:
dialogRef.value.open({ title: '编辑用户', data: row }) - 这样既复用了组件,又让 ref 管理的状态“带上下文”,不是简单布尔切换
4. 注意事项:别混淆 ref 的两种用途
同一个 ref 名字可能承担不同角色,容易出错:
-
DOM ref:
<div ref="domRef"> → 拿到原生 DOM 元素 <li> <font color="red">组件 ref</font>:<code><mydialog ref="dialogRef"></mydialog>→ 拿到组件实例(含 expose 的方法和属性) - 两者不能混用;组件 ref 必须搭配
defineExpose才有意义










