返回动态
5 分钟阅读

深入解析 addEventListener 的高级配置:从基础到 Vue3 实践

在 JavaScript 事件处理中,addEventListener 的第三个参数 options 提供了强大的控制能力。本文将解析现代浏览器支持的四个核心配置参数:captureoncepassivesignal,通过实际场景示例展示它们的妙用。

前言

在 JavaScript 事件处理中,addEventListener 的第三个参数 options 提供了强大的控制能力。本文将解析现代浏览器支持的四个核心配置参数:captureoncepassivesignal,通过实际场景示例展示它们的妙用。

一、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);

典型应用场景

  1. 页面跳转时自动清理事件

  2. 组件销毁时移除相关监听

  3. 条件性停止事件处理(如:游戏结束)


综合应用示例

实现高性能无限滚动

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();
});

参数兼容性参考表


最佳实践建议

  1. 优先使用对象参数:提高代码可读性

  2. 善用 passive 优化:特别是 touchwheel 事件

  3. 及时清理监听:结合 oncesignal 防止内存泄漏

  4. 渐进增强:为不支持新特性的浏览器提供降级方案


一、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 事件系统最佳实践

  1. 优先使用Vue原生修饰符 对于简单场景,优先使用 .once.passive 等内置修饰符

  2. 组合式API管理复杂监听 使用 onMounted + onUnmounted 生命周期钩子确保资源清理

  3. 响应式事件控制 结合 watchAbortController 实现动态事件管理

  4. 性能关键路径使用 passivetouchwheelscroll 等事件始终添加 { passive: true }

  5. 自定义事件传播控制 利用 capture 参数处理跨组件的事件优先级问题

评论