CustomEvent 构造函数必须用 detail 字段封装数据,其他字段无效;detail 可为任意类型,但传对象建议用 structuredClone() 预处理;需调用 dispatchEvent() 触发,监听须早于触发且事件名严格匹配;不跨 iframe;在 Vue/React 中可用但需注意与框架事件系统隔离。
必须用 detail 字段封装数据,其他字段不会被自动透传。浏览器只认这个键名,哪怕你写成 data 或 payload,接收方拿到的也是 undefined。
detail 可以是任意类型:字符串、对象、数组、数字,甚至 null 或 undefined
new CustomEvent('foo', { detail: myObj }) 后再修改 myObj —— 事件触发时取的是快照值,不是引用structuredClone())预处理,避免接收方意外修改原始数据常见原因是没调用 dispatchEvent(),或者事件名拼错导致监听和触发不匹配。也可能是事件在监听器注册前就已触发(比如 DOM 尚未 ready)。
const evt = new CustomEvent('user-login', { detail: { id: 123, token: 'abc' } });
window.dispatchEvent(evt); // 忘了这行?if (e.type === 'user-login'),别依赖 e.detail 做判断addEventListener 放在 DOMContentLoaded 之后,或用 setTimeout(.
.., 0) 延迟触发作验证不行。默认情况下 CustomEvent 不跨 iframe 边界,即使同源也不自动透出。这是浏览器安全模型决定的,不是 API 缺陷。
iframe.contentWindow.postMessage() 替代,配合 message 事件收发// 父页
iframe.contentWindow.dispatchEvent(new CustomEvent('sync', { detail: data }));
// 子页需自己监听 window 并转发到 document 或自定义目标
window.addEventListener('sync', e => document.dispatchEvent(e));postMessage 的 targetOrigin 参数不能写 '*'(有安全风险),应明确指定源能,但要注意框架的事件系统和原生事件系统是两套机制,混用容易丢失响应链或触发时机异常。
$emit 是组件内通信,CustomEvent 适合跨组件边界(如插件、微前端)或与非 Vue 代码交互useEffect 里加监听没问题,但记得在卸载时 removeEventListener,否则闭包会持有过期 stateCustomEvent,可能引发无限循环 —— 比如点击按钮触发 click,又在 handler 里发 click 自定义事件e.detail = ... 无效),且 CustomEvent 实例一旦创建,detail 就固定了;想动态改,只能新建事件重发。