自定义事件需同时设置 bubbles: true 和 composed: true 才能在捕获阶段被 document 监听;推荐统一用 audit-trigger 事件名,配合 data-audit-level 属性标记敏感等级,并在 document 上全局捕获以兼容 Shadow DOM 和 Portal 渲染。

捕获阶段监听自定义事件无法直接实现——因为自定义事件默认不可冒泡、不可捕获,必须显式配置 composed: true 和 bubbles: true 才能进入标准事件流,进而被 document.addEventListener('xxx', handler, true) 在捕获阶段捕获。
自定义事件必须手动启用事件流支持
浏览器原生的 click、keydown 等事件天然支持捕获与冒泡,但 new CustomEvent() 默认不具备该能力。若不配置,它只在触发目标节点上同步执行,不会经过 document → body → … → target 的路径,捕获监听器根本收不到。
- 正确写法(在 dispatch 前):
const event = new CustomEvent('user-delete-request', {<br> bubbles: true,<br> composed: true,<br> detail: { userId: 'u123' }<br>});<br>element.dispatchEvent(event); -
composed: true是关键:让事件穿透 Shadow DOM 边界,确保 Portal 渲染、Web Component 内部触发的事件也能被 document 捕获 - 缺一不可:仅设
bubbles: true不够,跨影子树场景下仍会丢失;仅设composed: true但bubbles: false,则捕获阶段可收到,但冒泡阶段收不到——而多数审计逻辑需两者兼顾
全局捕获需统一注册点与语义标记
不能靠监听所有自定义事件名硬编码匹配,而应聚焦行为语义。推荐用固定事件名 + 属性标记组合方式,便于审计策略集中管理。
- 统一使用
audit-trigger作为事件类型,避免分散监听多个名称(如pay-submit、data-export) - 通过
data-audit-level="critical"或data-audit-scope="finance"等属性标注敏感等级与业务域 - 捕获处理器中递归检查
event.target及其父级是否含data-audit-level,比解析 detail 更稳定(不受 i18n、动态 key 名影响)
审计逻辑必须区分“拦截”与“记录”,避免阻塞正常流程
前置审计的目标是可观测、可决策,不是无差别阻断。尤其对非破坏性操作(如导出预览、权限申请),应默认放行并仅上报,仅对高危动作(删除、转账、授权第三方)才做同步确认或暂停。
- 对
data-audit-level="high"事件:调用event.preventDefault()阻止后续默认行为,并弹出轻量确认浮层(非 modal 阻塞) - 对
data-audit-level="info"事件:仅采集event.detail、时间戳、用户身份、页面 URL,异步上报至审计服务 - 严禁在捕获处理器中调用
stopImmediatePropagation()—— 否则会覆盖组件内注册的同名事件监听器,导致功能异常
兼容 Shadow DOM 与框架 Portal 渲染
React Portal、Vue Teleport、Web Components 的 Shadow Root 会截断事件传播链。即使设置了 composed: true,若监听位置错误,仍可能漏掉。
- 监听必须绑定在
document,而非document.body或某个容器元素——body 下的 Portal 元素、Shadow DOM 外部挂载的浮层,都可能不在其子树内 - 验证方式:在 Shadow DOM 内 dispatch 一个
composed: true事件,看document.addEventListener('xxx', ..., true)是否触发 - 若使用微前端,主应用需确保子应用触发的自定义事件也满足
composed + bubbles,否则基座无法统一捕获










