5 分钟阅读
深入解析 addEventListener 的高级配置:从基础到 Vue3 实践
在 JavaScript 事件处理中,
addEventListener的第三个参数options提供了强大的控制能力。本文将解析现代浏览器支持的四个核心配置参数:capture、once、passive和signal,通过实际场景示例展示它们的妙用。
前言
在 JavaScript 事件处理中,addEventListener 的第三个参数 options 提供了强大的控制能力。本文将解析现代浏览器支持的四个核心配置参数:capture、once、passive 和 signal,通过实际场景示例展示它们的妙用。
一、capture:控制事件触发阶段
作用原理
// 默认冒泡阶段触发(false)
element.addEventListener("click", handler);
// 捕获阶段触发(true)
element.addEventListener("click", handler, { capture: true });
实际场景
多层级菜单的优先级处理 当需要父元素先于子元素处理点击事件时:
<div class="toolbar">
<button>保存</button>
<button>撤销</button>
</div>
<script>
document.querySelector('.toolbar').addEventListener('click', e => {
console.log('工具栏捕获点击');
}, { capture: true });
document.querySelectorAll('button').forEach(btn => {
btn.addEventListener('click', e => {
console.log('按钮点击');
});
});
</script>
输出顺序:工具栏捕获点击 → 按钮点击
二、once:一次性事件监听
使用场景
表单提交防重复
const form = document.getElementById('myForm');
form.addEventListener('submit', async (e) => {
e.preventDefault();
// 显示加载状态
submitButton.textContent = '提交中...';
try {
await fetch('/submit', { method: 'POST' });
showSuccessMessage();
} catch (err) {
showErrorMessage();
}
}, { once: true }); // 确保只执行一次
三、passive:性能优化利器
原理与效果
// 传统方式(可能阻塞滚动)
window.addEventListener('scroll', e => {
/* 可能包含 preventDefault() */
});
// 优化版本
window.addEventListener('scroll', e => {
// 这里不能有 preventDefault()
}, { passive: true });
四、signal:精准控制监听周期
结合 AbortController 使用
// 创建控制器
const controller = new AbortController();
// 监听按钮点击
const button = document.getElementById('myButton');
button.addEventListener('click', () => {
console.log('点击生效');
}, { signal: controller.signal });
// 5秒后自动取消监听
setTimeout(() => {
controller.abort();
console.log('监听已移除');
}, 5000);
典型应用场景
-
页面跳转时自动清理事件
-
组件销毁时移除相关监听
-
条件性停止事件处理(如:游戏结束)
综合应用示例
实现高性能无限滚动
const scrollController = new AbortController();
window.addEventListener('scroll', async () => {
const { scrollTop, clientHeight, scrollHeight } = document.documentElement;
if (scrollTop + clientHeight >= scrollHeight - 500) {
// 加载新数据
await loadMoreItems();
}
}, {
passive: true, // 确保滚动流畅
signal: scrollController.signal
});
// 离开页面时清理
window.addEventListener('beforeunload', () => {
scrollController.abort();
});
参数兼容性参考表
最佳实践建议
-
优先使用对象参数:提高代码可读性
-
善用 passive 优化:特别是
touch和wheel事件 -
及时清理监听:结合
once和signal防止内存泄漏 -
渐进增强:为不支持新特性的浏览器提供降级方案
一、Vue3 中访问原生事件参数
在模板中直接使用
<template>
<button @click="handleClick($event, '额外参数')">
点击测试
</button>
</template>
<script setup>
const handleClick = (event, msg) => {
console.log(event.target); // 原生事件对象
console.log(msg); // 额外参数
}
</script>
组合式API中访问事件
<script setup>
import { onMounted } from 'vue'
onMounted(() => {
const button = document.getElementById('myBtn');
button.addEventListener('click', (e) => {
console.log('Vue组件中的原生事件:', e);
}, {
passive: true,
capture: false
});
});
</script>
二、Vue3 特有事件处理技巧
1. 事件修饰符与配置参数映射
<!-- 等效于 { capture: true, passive: true } -->
<button @click.capture.passive="handleClick">
修饰符组合
</button>
2. 自动清理的事件监听
<script setup>
import { onMounted, onUnmounted } from 'vue'
const controller = new AbortController();
onMounted(() => {
window.addEventListener('resize', handleResize, {
signal: controller.signal,
passive: true
});
});
onUnmounted(() => {
controller.abort();
});
const handleResize = () => {
console.log('窗口大小变化');
}
</script>
三、性能优化实战
1. 滚动性能优化
<script setup>
import { onMounted } from 'vue'
onMounted(() => {
const list = document.querySelector('.virtual-list');
list.addEventListener('scroll', updateVirtualScroll, {
passive: true // 关键性能优化
});
});
const updateVirtualScroll = () => {
// 虚拟列表渲染逻辑
// 注意:这里不能有 preventDefault()
}
</script>
2. 一次性表单提交
<script setup>
import { ref } from 'vue'
const formRef = ref(null);
const isSubmitting = ref(false);
onMounted(() => {
formRef.value.addEventListener('submit', async (e) => {
e.preventDefault();
isSubmitting.value = true;
try {
await submitForm();
} finally {
isSubmitting.value = false;
}
}, { once: true }); // 防止重复提交
});
</script>
四、高级模式:动态事件管理
1. 条件式事件监听
<script setup>
import { ref, watchEffect } from 'vue'
const isActive = ref(true);
const controller = new AbortController();
watchEffect(() => {
if (isActive.value) {
window.addEventListener('mousemove', trackMouse, {
signal: controller.signal,
passive: true
});
} else {
controller.abort();
}
});
const trackMouse = (e) => {
console.log(`鼠标位置: ${e.clientX}, ${e.clientY}`);
}
</script>
2. 组件间事件传递优化
<!-- ParentComponent.vue -->
<template>
<ChildComponent @custom-event.capture="handleGlobalEvent" />
</template>
<script setup>
const handleGlobalEvent = (payload) => {
console.log('捕获阶段的跨组件事件:', payload);
}
</script>
五、Vue3 事件系统最佳实践
-
优先使用Vue原生修饰符 对于简单场景,优先使用
.once、.passive等内置修饰符 -
组合式API管理复杂监听 使用
onMounted+onUnmounted生命周期钩子确保资源清理 -
响应式事件控制 结合
watch和AbortController实现动态事件管理 -
性能关键路径使用 passive 对
touch、wheel、scroll等事件始终添加{ passive: true } -
自定义事件传播控制 利用
capture参数处理跨组件的事件优先级问题
评论