用Web Components封装轻量级模态弹窗,核心是原生<dialog>+自定义元素:以<dialog>为底层容器(需挂载body下),采用包裹模式(因Safari不支持继承HTMLDialogElement),通过open属性和close事件同步状态,命名slot划分语义区域,兼容Safari需降级处理backdrop、焦点锁及无障碍属性,暴露open、showModal()、close()简洁API,样式通过::part()或CSS变量定制。

用 Web Components 实现轻量级模态弹窗,核心是原生 <dialog> + 自定义元素封装,不依赖框架、体积小、语义清晰。关键不在“写个弹窗”,而在“封装可控、可复用、无障碍、兼容好”。
用
<dialog> 是浏览器原生支持的模态语义元素,自带 showModal()、close()、ESC 关闭、焦点锁定等能力,比手写遮罩层更轻、更可靠。
- 必须挂载到
document.body下,避免被父级overflow: hidden或 transform 截断 - 不能继承
HTMLDialogElement(Safari 不支持),所以采用包裹模式:<my-dialog><dialog>…</dialog></my-dialog></li> <li>通过 <code>open
属性同步控制显隐,同时监听close事件同步内部状态
用 slot 划分语义区域
通过命名 slot 让使用者自由注入内容,同时保持组件封装性:
-
<slot name="header">:标题区,支持图标、关闭按钮等 -
<slot name="body">:主体内容,可放表单、文本、异步加载区块 -
<slot name="footer">:操作区,支持确认/取消按钮或自定义控件 - 配合
slotchange事件可动态响应内容变更,比如自动聚焦首个可交互元素
补足 Safari 兼容与基础体验
Safari 目前不支持 <dialog> 的 backdrop 和部分 API,需降级处理:
- 检测
'showModal' in HTMLDialogElement.prototype判断原生支持度 - 不支持时,手动插入半透明 backdrop 元素,并用
inert属性禁用背景交互 - 手动实现焦点锁(
focusin拦截 + 循环聚焦内部可 tab 元素) - 为
<dialog>添加aria-modal="true"和role="dialog",确保读屏器识别
暴露简洁的 JS API
组件对外只暴露最必要的接口,降低使用门槛:
-
open属性(双向绑定,支持v-model或.open = true) -
showModal()方法:触发原生模态行为(含 backdrop 显示、焦点锁定) -
close()方法:触发关闭流程(含触发close事件、清理焦点) - 不暴露 Shadow DOM 内部结构,所有样式通过
::part()或 CSS 变量定制(如--my-dialog-width)
不复杂但容易忽略——真正轻量,不是代码行数少,而是不侵入页面布局、不污染全局、不绑架使用者结构。用好 <dialog> 和 slot,再补一点兼容逻辑,一个现代、健壮、可嵌入任意项目的模态组件就完成了。


















