优雅的前端弹窗和确认框的实现
比较局部业务弹窗、全局 Promise 确认服务与 Toast 通知的职责、上下文和生命周期边界。
#synthesis
#growing
#frontend
[!info] related notes
- 接入:全局弹窗
- 实现:ConfirmDialog 实现
- 排障:Vue 全局对话框状态不共享
优雅的前端弹窗和确认框的实现
三种需求分开处理
| 需求 | 常见选择 | 需要保留的边界 |
|---|---|---|
| 复杂业务表单 | 局部声明式组件,以 open 状态控制 | 表单数据、校验与组件生命周期 |
| 简单确认 | Promise 驱动的共享确认服务 | 每个请求独立结束,取消不能执行动作 |
| 成功或错误通知 | Toast / Sonner | 通知不是等待批准的确认框 |
确认服务的两种接入
动态 createVNode/render 挂载可减少根组件声明,但需要显式处理应用上下文、卸载与 DOM 清理。继承不到 provide、Pinia 或 Router 时不能假定组件会自动获得实例。
根组件挂载一个 Provider 的方式更容易共享上下文。调用方提交请求,Provider 渲染,确认或取消后结束对应 Promise。共享一个 resolver 时必须拒绝新请求、排队或采用明确替换策略;直接覆盖会让旧 Promise 永久等待。
具体 Vue/Vuetify 实现见 ConfirmDialog 实现;这两种接入方式没有对所有项目都成立的“最优”结论。
Toast 库身份不能靠属性猜测
早期 DailyUse 记录提到 @dailyuse/ui-vue-shadcn 的 Toaster、duration 与 rich-colors。仅凭组件名或透传属性不能证明底层一定是 Sonner,应检查组件导出、依赖版本与封装源码。
React 的 sonner 与 Vue 对应封装不是同一个导入路径。迁移时核对真实组件库,不把 Vue 的 Toast 和确认 Dialog 混成一种 API。
生命周期与可访问性
确认、取消、关闭、Provider 卸载都要结束待处理请求。SSR 下服务按应用/请求创建,不能让进程级单例混用用户状态。复杂表单、Toast 和确认框也分别需要名称、焦点与键盘行为,见 ARIA。