共计 2569 个字符,预计需要花费 7 分钟才能阅读完成。
基础用法回顾
addEventListener是前端开发中最常用的 DOM API 之一,它的完整签名如下:
element.addEventListener(type, listener[, options|useCapture]);

- 第一个参数 type:字符串,表示要监听的事件类型,如 ’click’、’scroll’ 等
- 第二个参数 listener:事件触发时执行的回调函数
- 第三个参数:可以是布尔值 useCapture 或配置对象 options
第三个参数的两种形式
1. 布尔值形式(传统用法)
当传入布尔值时,它表示是否在捕获阶段触发事件处理程序:
// 冒泡阶段触发(默认)document.addEventListener('click', handler, false);
// 捕获阶段触发
document.addEventListener('click', handler, true);
2. 对象形式(现代用法)
较新的浏览器支持传入配置对象,可包含以下属性:
capture: 布尔值,等同于旧的 useCaptureonce: 布尔值,表示是否只执行一次passive: 布尔值,表示监听器是否会调用 preventDefault()signal: 可选 AbortSignal,用于移除监听器
passive 选项的性能优化
问题背景
当监听滚动事件时,浏览器无法预先知道我们是否会调用preventDefault(),因此必须等待事件处理程序执行完毕才能进行滚动,导致性能问题。
性能对比测试
// 测试代码
const testPassive = (usePassive) => {const el = document.createElement('div');
document.body.appendChild(el);
let startTime;
el.addEventListener('scroll', () => {if (!startTime) startTime = performance.now();}, usePassive ? {passive: true} : false);
// 模拟滚动
const start = performance.now();
for (let i = 0; i < 100000; i++) {el.scrollTop = i;}
console.log(`passive=${usePassive},耗时:${performance.now() - start}ms`);
};
// 执行测试
testPassive(false);
testPassive(true);
测试结果通常会显示 passive: true 能显著提升滚动性能(在我的测试中提升约 30%)。
其他选项详解
once 选项
// 只执行一次的监听器
button.addEventListener('click', () => {console.log('只会执行一次!');
}, {once: true});
适用场景:
– 表单提交按钮
– 一次性引导提示
– 只需要触发一次的事件
capture 选项
// 在捕获阶段处理事件
document.addEventListener('click', handler, { capture: true});
适用场景:
– 需要比其他元素更早拦截事件
– 实现事件代理时控制处理顺序
生产环境避坑指南
-
不要混用两种形式
// 错误用法 element.addEventListener('scroll', handler, { passive: true, capture: false}); element.addEventListener('scroll', handler, false); // 会覆盖上面的配置 -
移动端优先使用 passive
- 特别是 touch 和 wheel 事件
-
但确保不需要阻止默认行为
-
注意事件移除
-
使用对象配置时,移除监听器必须使用相同的配置
// 正确的移除方式 const opts = {passive: true}; el.addEventListener('scroll', handler, opts); el.removeEventListener('scroll', handler, opts); // 配置必须一致 -
浏览器兼容性处理
// 兼容性封装 function addEventListenerWithOptions(target, type, listener, options) { const opts = typeof options === 'boolean' ? {capture: options} : options || {}; if (opts.passive && !supportsPassive) {opts.passive = false; // 降级处理} target.addEventListener(type, listener, opts); }
完整配置示例
// 最佳实践示例
const button = document.getElementById('myButton');
// 一次性、被动式、在捕获阶段处理的点击事件
button.addEventListener('click', (e) => {console.log('按钮被点击了!');
// 注意:由于设置了 passive:true,这里调用 e.preventDefault()会抛出警告}, {
capture: true,
passive: true,
once: true
});
// 带中止控制的滚动监听
const controller = new AbortController();
window.addEventListener('scroll', () => {console.log('页面滚动中...');
}, {
passive: true,
signal: controller.signal
});
// 需要时取消监听
controller.abort();
在 SPA 中的优化思考
在单页应用中,合理利用这些选项可以:
- 对全局滚动监听使用
passive: true提升滚动性能 - 对临时性事件使用
once: true减少内存泄漏风险 - 使用
AbortController统一管理组件卸载时的事件清理 - 对嵌套组件使用
capture控制事件处理顺序
通过深入理解 addEventListener 的第三个参数,我们可以显著提升 Web 应用的性能和可维护性。建议在实际项目中根据具体场景灵活组合这些选项,并做好兼容性处理。
正文完
发表至: 前端开发
近一天内
