用 new CustomEvent 创建自定义事件时,必须通过 detail 选项传入业务数据,再调用 dispatchEvent 触发;监听方从 event.detail 安全读取,detail 是唯一推荐的数据载体,支持任意类型但需注意跨上下文限制。

用 new CustomEvent 创建携带业务数据的自定义事件,关键在于正确使用 detail 选项传入任意结构的数据对象,再通过 dispatchEvent 触发,监听方从事件对象的 detail 属性中读取。
detail 是唯一推荐的数据载体
CustomEvent 构造函数支持一个可选的配置对象,其中 detail 是唯一用于传递自定义业务数据的字段。它没有类型限制,可以是字符串、数字、数组、普通对象,甚至 Class 实例(需注意序列化限制)。
- ✅ 推荐写法:
new CustomEvent('user-login', { detail: { id: 123, name: 'Alice', roles: ['admin'] } }) - ❌ 避免在
detail外额外挂属性,比如{ detail: ..., userId: 456 }—— 这些不会被自动注入到事件实例上 - ⚠️ 注意:
detail中若含函数、DOM 节点或循环引用,在跨上下文(如 iframe、Web Worker)传递时可能丢失或报错
触发事件要绑定到有效目标节点
自定义事件需调用 dispatchEvent 方法才能真正发出,且必须作用于一个有效的 DOM 节点(如 document、window 或某个元素),不能直接对事件对象调用。
- 常见目标选择:
document.dispatchEvent(evt)(全局广播)、myButton.dispatchEvent(evt)(组件内通信) - 确保目标节点已存在于 DOM 中,否则会静默失败(不报错但监听器收不到)
- 如果需要冒泡,构造时设
bubbles: true;需要被stopPropagation()拦截,就设cancelable: true
监听时从 event.detail 安全取值
监听函数中,业务数据始终位于 event.detail,建议做存在性判断,避免因未传参导致 undefined 报错。
- 基础监听写法:
document.addEventListener('user-login', e => console.log(e.detail.name)) - 防御性写法:
const data = e.detail ?? {}; console.log(data.id || 'unknown') - 配合 TypeScript 可定义接口增强类型提示,例如:
interface LoginDetail { id: number; name: string; },然后e.detail as LoginDetail
实际小例子:登录成功后通知全局
假设用户登录完成,前端需要通知多个模块更新状态:
// 发送方(如登录表单提交后)
const loginSuccess = new CustomEvent('auth-success', {
detail: { token: 'abc123', user: { id: 789, avatar: '/u.png' } },
bubbles: true
});
document.dispatchEvent(loginSuccess);
// 监听方(如用户头像组件)
document.addEventListener('auth-success', e => {
const { token, user } = e.detail;
localStorage.setItem('auth-token', token);
renderAvatar(user.avatar);
});

















