前端Vue中,如何实现父组件打开子组件窗口等待子组件窗口关闭后继续执行父组件逻辑
1.需求:
- 当执行 this.isOpenArchives = true 打开子组件弹窗时,等待子组件关闭并回传数据后,才继续往下执行后续逻辑(即实现“同步等待”效果)。
- 子组件关闭时能回传数据给父组件。
2.流程详细概要:
2.1.父组件中打开子组件窗口
async uf_add_brda(as_klx, as_kh, as_sfzh){
// 打开子组件窗口
this.isOpenArchives = true;
// data 就是子组件回传的数据
const { confirmed, data } = await this.showArchivesDialog();
// confirmed 为true是sucess调用,反之为close调用
if (!confirmed) return;// 取出data中封装的数据
lstr_out = data.lstr_mess// 这里写后续逻辑
// .
// .
// .
}
const { confirmed, data } = await this.showArchivesDialog();
if (confirmed) {
// data 就是子组件回传的数据
// 这里写后续逻辑
}
这样,只有等子组件关闭并回传数据后,后续代码才会继续执行,完全等价于 PB 的同步弹窗+数据回传。
2.1.1在组件中引入子组件
// 省略
// .
// .
// .
<m-dialog :title="dialogTitle" v-model.sync="isOpenArchives" top="10vh" show-footer :closeOnModal="false"
:destroyOnClose="false">
<Archives v-if="isOpenArchives" :inTheDialog="true" :pget="pget" ref="archives" @close="isOpenArchives = false" @success="isOpenArchives = false">
</Archives>
</m-dialog>
// 省略
// .
// .
// .
import MDialog from "../../../components/MForm/MDialog.vue";
import Archives from "../archives/index.vue";
// 省略
// .
// .
// .
export default {
components: {
DynamicQuery,
OutpatientReturnReg,
Archives,
FpSet,
MDialog,
Charge,
},
// 省略
// .
// .
// .
}
2.1.2封装弹窗显示方法
// 封装弹窗显示方法(返回Promise)
async showArchivesDialog() {
// 打开子组件弹窗
this.isOpenArchives = true;
await this.$nextTick(); // 关键!
if (!this.$refs.archives) {
return { confirmed: false };
}
return new Promise((resolve) => {
this.$refs.archives.$once('success', (data) => {
this.isOpenArchives = false;
resolve({ confirmed: true, data });
});
this.$refs.archives.$once('close', () => {
this.isOpenArchives = false;
resolve({ confirmed: false });
});
});
},
1. 不要直接用 this.isOpenArchives = true,而是用 await this.showArchivesDialog()。
2. 子组件(Archives)里,关闭弹窗时用 $emit('success', data) 或 $emit('close'),父组件就能收到。
2.1.3子组件代码
<template>
<Container leftWidth="100%" :topVisibel="!inTheDialog">
<template #top>
<div>
<m-button name="save" :disabled="saveDisabled" :loading="saveLoading" :debounceTime="1000" :isDebounce="true" @click="save"
>保存</m-button
>
<m-button
name="save"
:disabled="saveDisabled"
:loading="saveLoading"
:isDebounce="true"
:debounceTime="1000"
@click="save(1)"
v-if="currentId === ''"
>保存并关闭</m-button
>
<m-button name="close" @click="$tab.closePage()">关闭</m-button>
</div>
</template>
<template #left>
<div class="left-layout" v-loading="formLoading">
<div class="footer" v-if="inTheDialog">
<div>
<m-button name="" v-if="currentId === ''" @click="clearForm"
>清空</m-button
>
<el-checkbox
v-model="isShowJhInfo"
style="margin-left: 20px"
class="btn-ml"
>监护人信息</el-checkbox
>
</div>
<div>// 下面的save和close方法处理完成后会关闭窗口,将数据返回给父窗口
<m-button name="save" :isDebounce="true" :debounceTime="1000" @click="save" :loading="saveLoading">保存</m-button>
<m-button name="close" @click="close">关闭</m-button>
</div>
</div>
</div>
</template>
<m-dialog
:title="dialogTitle"
v-model.sync="showDAXZ"
top="10vh"
show-footer
appendToBody
:closeOnModal="false"
:destroyOnClose="false"
>
<archivesDAXZ
v-if="showDAXZ"
:pget="daxzPget"
:inTheDialog="true"
ref="daxzRef"
@copySuccess="copySuccess"
@close="showDAXZ=false">
</archivesDAXZ>
</m-dialog></Container>
</template>
save方法
async save(isSave) {try{await jdbq_jd(modifyParams).then((res) => {this.saveLoading = false;this.$message.success("保存成功");// 调用父组件中的success方法并返回数据this.$emit("success", res.data);if (isSave === 1) {this.$tab.closePage();}if (!this.inTheDialog) {this.clearForm();}});}finally{this.saveLoading = false;}// 关闭页面this.$emit("close");},
close方法
close() {
// 调用父组件中的close方法this.$emit("close");
},
3.子组件如何回传数据?
在 Archives 组件里,关闭弹窗时这样写:
// 成功时
this.$emit('success', data);
// 取消时
this.$emit('close');
4.补充解释this.$refs.archives.$once
这段代码 this.$refs.archives.$once
是 Vue.js 中的语法,其含义如下:
4.1 解释this.$refs.archives
this
:指向当前 Vue 组件实例。
$refs
:Vue 的特殊属性,用于访问模板中通过ref
注册的 DOM 元素或子组件。
archives
:模板中定义的ref
名称,例如:
<child-component ref="archives" />
<!-- 或 -->
<div ref="archives"></div>
结果:
this.$refs.archives
获取名为archives
的子组件实例或 DOM 元素(通常用于访问子组件)。
4.2 解释$once
$once
是 Vue 实例的方法,用于监听一个自定义事件,但仅触发一次。触发后监听器会自动移除。语法:
vm.$once(eventName, callback)
eventName
:要监听的事件名称(字符串)。callback
:事件触发时执行的回调函数。
4.3完整含义
this.$refs.archives.$once
表示:
在子组件 archives
上注册一个一次性事件监听器。当 archives
子组件内部触发指定事件时,父组件的回调函数会执行一次,随后自动解除监听。
4.4典型使用场景
// 父组件中
this.$refs.archives.$once('some-event', (payload) => {
console.log('事件仅触发一次!', payload);
});
子组件如何触发?
在子组件(archives
)内部,通过 $emit
触发事件:
this.$emit('some-event', { data: '传递的数据' });
4.5⚠️ 注意事项
Vue 3 的变化在 Vue 3 中,
使用$on
、$once
和$off
已被移除(RFC)。替代方案:v-on:event.once
在模板中:
<child-component @some-event.once="handleEvent" />
手动实现一次性监听(在 setup 或方法中):
import { onMounted } from 'vue';
setup() {
const handleEventOnce = (payload) => {
console.log(payload);
// 移除监听逻辑...
};
// 在适当位置手动调用 $on 并移除(需自己实现)
2. 确保 $refs
可用$refs
在组件渲染完成后才填充,避免在 created
生命周期中使用(建议在 mounted
或事件回调中使用)。
4.6总结
this.$refs.archives.$once
:在子组件archives
上绑定一个一次性事件监听器。用途:适用于只需响应一次的事件(如弹窗关闭、初始化完成通知等)。
Vue 3 用户:改用模板的
.once
修饰符或手动事件管理。
5.总结
- 用 await this.showArchivesDialog() 实现“同步等待弹窗关闭并回传数据”。
- 子组件用 $emit('success', data) 或 $emit('close') 回传数据/关闭信号。