弹窗组件应仅通过 emit 抛出 confirm/cancel/update:visible 等语义化事件,由父组件统一处理业务逻辑;子组件不调用 api、不跳转、不改全局状态,按钮交由 slot 实现,确保解耦、可复用、易测试。

弹窗的“取消/确定”逻辑不该由弹窗自己决定下一步做什么,而应通过 emit 把用户意图明确抛出,由父组件统一承接和执行。这样既解耦、可复用,又便于测试和状态追踪。
子组件只 emit 语义化事件,不执行业务
弹窗组件(如 BaseDialog.vue)内部不调用 API、不跳转路由、不修改全局状态。它只在用户操作后发出清晰、有含义的事件:
-
@confirm:点击“确定”按钮时触发,可携带表单数据(如emit('confirm', { username, password })) -
@cancel:点击“取消”、遮罩层或 ESC 键时触发(注意:不混用close,语义要区分“主动放弃”与“被动关闭”) -
@update:visible:用于支持v-model:visible双向绑定,统一控制显隐
所有按钮都加 @click.stop,防止事件冒泡干扰外部点击判断逻辑。
父组件用 v-model + 事件监听接管流程
父组件用一个响应式变量(如 dialogVisible)控制弹窗开关,并通过 v-model:visible 自动同步显隐状态:
<basedialog v-model:visible="dialogVisible"></basedialog>
其中:
-
handleSubmit负责校验、提交、处理 loading 和错误反馈 -
handleCancel可重置表单、清空临时数据,再关闭弹窗 - 若需传递参数(如当前编辑项 ID),可在打开弹窗时传入 props,而非从 emit 中反向获取
Footer 按钮区域交给 slot,保持行为与样式分离
不要用 ok-text、cancel-disabled 等一堆 props 控制按钮,而是把底部交由具名 slot:
<basedialog v-model:visible="dialogVisible"><template><el-button false>取消</el-button><el-button type="primary" :loading="submitting">确定</el-button></template></basedialog>
这样父组件能完全掌控按钮文案、禁用逻辑、加载态、权限控制等细节,子组件只做容器,不越权。
边界情况靠 emit + 状态配合兜底
真实交互中容易遗漏的点,靠父子协同 + emit 显式传达来保障健壮性:
- 用户快速连点“确定” → 子组件内部用
:loadingprop 或按钮:disabled控制,同时 emit 前检查状态 - API 请求失败 → 父组件收到 error 后,可选择保留弹窗并提示错误,或 emit
'show-error'让子组件展示错误提示区 - 页面卸载前弹窗未关闭 → 父组件在
onBeforeUnmount中主动设dialogVisible = false,避免残留
不复杂但容易忽略。










