自定义事件(CustomEvent)
CustomEvent API 允许开发者创建和触发自定义事件,实现组件间解耦通信。
📚 目录
1. CustomEvent 概述
1.1 什么是自定义事件
CustomEvent 是浏览器提供的用于创建自定义事件的接口,允许开发者定义自己的事件类型,实现组件间解耦通信。
特点:
- 解耦通信:组件间无需直接引用,通过事件通信
- 灵活扩展:可以传递任意数据
- 标准 API:基于 DOM 事件系统,兼容性好
1.2 与原生事件的区别
| 特性 | 原生事件 | 自定义事件 |
|---|---|---|
| 触发方式 | 浏览器自动触发 | 开发者手动触发 |
| 事件类型 | 预定义(click、load 等) | 自定义命名 |
| 数据传递 | 通过 event 对象 | 通过 detail 属性 |
| 使用场景 | 用户交互、页面生命周期 | 组件通信、业务逻辑 |
2. 创建自定义事件
2.1 基本语法
// 创建自定义事件
const event = new CustomEvent('eventName', {
detail: {
// 自定义数据
},
bubbles: true, // 是否冒泡
cancelable: true // 是否可取消
});2.2 构造函数参数
CustomEvent(type, options)
- type(必需):事件类型名称(字符串)
- options(可选):事件配置对象
detail:传递给事件监听器的数据bubbles:是否冒泡(默认false)cancelable:是否可取消(默认false)
2.3 创建示例
// 创建简单自定义事件
const simpleEvent = new CustomEvent('myEvent');
// 创建带数据的自定义事件
const dataEvent = new CustomEvent('userAction', {
detail: {
action: 'click',
timestamp: Date.now()
}
});
// 创建可冒泡的自定义事件
const bubbleEvent = new CustomEvent('customClick', {
detail: { message: 'Hello' },
bubbles: true,
cancelable: true
});3. 触发自定义事件
3.1 在 window 上触发
// 创建自定义事件
const event = new CustomEvent('miniServiceAgreed', {
detail: {
userId: '123',
timestamp: Date.now()
}
});
// 在 window 上触发
window.dispatchEvent(event);3.2 在 DOM 元素上触发
// 在特定元素上触发
const button = document.querySelector('#myButton');
const event = new CustomEvent('buttonClicked', {
detail: { buttonId: 'myButton' }
});
button.dispatchEvent(event);3.3 在 document 上触发
// 在 document 上触发
const event = new CustomEvent('pageReady', {
detail: { page: 'home' }
});
document.dispatchEvent(event);4. 监听自定义事件
4.1 使用 addEventListener
// 监听 window 上的自定义事件
window.addEventListener('miniServiceAgreed', (event) => {
console.log('用户已同意服务协议');
console.log('事件数据:', event.detail);
});
// 监听 DOM 元素上的自定义事件
const button = document.querySelector('#myButton');
button.addEventListener('buttonClicked', (event) => {
console.log('按钮被点击:', event.detail);
});4.2 获取事件数据
window.addEventListener('miniServiceAgreed', (event) => {
// 通过 detail 属性获取数据
const { userId, timestamp } = event.detail;
console.log('用户ID:', userId);
console.log('时间戳:', timestamp);
});4.3 事件对象属性
window.addEventListener('customEvent', (event) => {
// 事件类型
console.log(event.type); // 'customEvent'
// 自定义数据
console.log(event.detail); // 传递的数据对象
// 事件目标
console.log(event.target); // 触发事件的元素
// 当前目标
console.log(event.currentTarget); // 绑定监听器的元素
// 是否冒泡
console.log(event.bubbles); // true/false
// 是否可取消
console.log(event.cancelable); // true/false
});5. 事件数据传递
5.1 传递简单数据
// 触发事件时传递数据
const event = new CustomEvent('dataEvent', {
detail: {
message: 'Hello World',
count: 42
}
});
window.dispatchEvent(event);
// 接收数据
window.addEventListener('dataEvent', (event) => {
console.log(event.detail.message); // 'Hello World'
console.log(event.detail.count); // 42
});5.2 传递复杂对象
// 传递复杂对象
const event = new CustomEvent('userEvent', {
detail: {
user: {
id: '123',
name: 'John',
email: 'john@example.com'
},
action: 'login',
metadata: {
ip: '192.168.1.1',
userAgent: navigator.userAgent
}
}
});
window.dispatchEvent(event);
// 接收复杂对象
window.addEventListener('userEvent', (event) => {
const { user, action, metadata } = event.detail;
console.log('用户:', user.name);
console.log('操作:', action);
console.log('元数据:', metadata);
});5.3 传递函数(不推荐)
// 注意:detail 中传递函数会丢失上下文
const event = new CustomEvent('callbackEvent', {
detail: {
callback: function() {
console.log('回调执行');
}
}
});
window.dispatchEvent(event);
// 接收函数
window.addEventListener('callbackEvent', (event) => {
// 可以调用,但需要注意 this 绑定
event.detail.callback();
});6. 实际应用场景
6.1 组件间通信
// 组件 A:触发事件
class ComponentA {
handleAgree() {
const event = new CustomEvent('serviceAgreed', {
detail: {
userId: this.userId,
timestamp: Date.now()
}
});
window.dispatchEvent(event);
}
}
// 组件 B:监听事件
class ComponentB {
constructor() {
window.addEventListener('serviceAgreed', this.onServiceAgreed.bind(this));
}
onServiceAgreed(event) {
const { userId, timestamp } = event.detail;
console.log('用户同意服务协议:', userId);
// 执行相应逻辑
}
}6.2 状态同步
// 状态管理:触发状态变更事件
function updateUserStatus(status) {
const event = new CustomEvent('userStatusChanged', {
detail: {
status: status,
previousStatus: currentStatus,
timestamp: Date.now()
}
});
window.dispatchEvent(event);
}
// 多个组件监听状态变更
window.addEventListener('userStatusChanged', (event) => {
const { status } = event.detail;
updateUI(status);
updateAnalytics(status);
updateCache(status);
});6.3 跨框架通信
// React 组件触发事件
function ReactComponent() {
const handleClick = () => {
const event = new CustomEvent('reactAction', {
detail: { action: 'buttonClick' }
});
window.dispatchEvent(event);
};
return <button onClick={handleClick}>Click</button>;
}
// Vue 组件监听事件
export default {
mounted() {
window.addEventListener('reactAction', this.handleReactAction);
},
methods: {
handleReactAction(event) {
console.log('React 触发的操作:', event.detail.action);
}
}
}6.4 微前端通信
// 主应用:触发全局事件
function notifyMicroApp(data) {
const event = new CustomEvent('microAppEvent', {
detail: data,
bubbles: true
});
window.dispatchEvent(event);
}
// 微应用:监听全局事件
window.addEventListener('microAppEvent', (event) => {
const data = event.detail;
// 处理来自主应用的数据
handleMicroAppData(data);
});7. 最佳实践
7.1 事件命名规范
// ✅ 好的命名:清晰、有意义的名称
new CustomEvent('userLoginSuccess');
new CustomEvent('cartItemAdded');
new CustomEvent('paymentCompleted');
// ❌ 不好的命名:模糊、无意义
new CustomEvent('event1');
new CustomEvent('action');
new CustomEvent('update');7.2 使用命名空间
// 使用命名空间避免冲突
new CustomEvent('app:user:login');
new CustomEvent('app:cart:add');
new CustomEvent('app:payment:complete');
// 监听时使用命名空间
window.addEventListener('app:user:login', handler);7.3 事件清理
// 组件销毁时移除事件监听
class MyComponent {
constructor() {
this.handleEvent = this.handleEvent.bind(this);
window.addEventListener('customEvent', this.handleEvent);
}
handleEvent(event) {
// 处理事件
}
destroy() {
// 清理事件监听
window.removeEventListener('customEvent', this.handleEvent);
}
}7.4 错误处理
window.addEventListener('customEvent', (event) => {
try {
// 处理事件
processEvent(event.detail);
} catch (error) {
console.error('处理自定义事件时出错:', error);
// 错误上报
reportError(error);
}
});7.5 类型检查
// 触发事件前验证数据
function triggerEvent(eventName, data) {
if (!eventName || typeof eventName !== 'string') {
throw new Error('事件名称必须是字符串');
}
const event = new CustomEvent(eventName, {
detail: data,
bubbles: true,
cancelable: true
});
window.dispatchEvent(event);
}
// 监听事件时验证数据
window.addEventListener('customEvent', (event) => {
if (!event.detail || typeof event.detail !== 'object') {
console.warn('事件数据格式不正确');
return;
}
// 处理事件
});7.6 性能优化
// 使用防抖处理高频事件
function debounce(func, wait) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), wait);
};
}
// 防抖处理自定义事件
const debouncedHandler = debounce((event) => {
console.log('处理事件:', event.detail);
}, 300);
window.addEventListener('highFrequencyEvent', debouncedHandler);8. 与其他通信方式对比
8.1 CustomEvent vs 回调函数
| 特性 | CustomEvent | 回调函数 |
|---|---|---|
| 解耦性 | 高(发布订阅模式) | 低(直接调用) |
| 一对多 | 支持 | 不支持 |
| 异步性 | 支持 | 需要手动处理 |
| 适用场景 | 组件间通信 | 简单函数调用 |
8.2 CustomEvent vs EventEmitter
// CustomEvent:浏览器原生 API
const event = new CustomEvent('myEvent', { detail: data });
window.dispatchEvent(event);
// EventEmitter:Node.js 风格(需要库支持)
const emitter = new EventEmitter();
emitter.emit('myEvent', data);8.3 CustomEvent vs 全局状态管理
// CustomEvent:事件驱动
window.dispatchEvent(new CustomEvent('stateChange', { detail: newState }));
// 全局状态管理:直接访问状态
store.setState(newState);
const state = store.getState();9. 兼容性
9.1 浏览器支持
- Chrome:15+
- Firefox:11+
- Safari:6+
- Edge:12+
- IE:不支持(需要使用 polyfill)
9.2 Polyfill
// 简单的 CustomEvent polyfill
(function() {
if (typeof window.CustomEvent === 'function') {
return;
}
function CustomEvent(type, params) {
params = params || { bubbles: false, cancelable: false, detail: null };
const event = document.createEvent('CustomEvent');
event.initCustomEvent(type, params.bubbles, params.cancelable, params.detail);
return event;
}
window.CustomEvent = CustomEvent;
})();10. 相关链接
10.1 相关文档
10.2 参考资源
最后更新:2025
维护规范:每次新增笔记后,在对应 MOC 中加入链接