事件的 捕获模式(Capture Phase) 是 DOM 事件流中的一个阶段,与 冒泡模式(Bubble Phase) 共同构成完整的事件传播流程。以下是详细解析:
1. 事件流的三个阶段
当 DOM 事件触发时(如点击元素),事件会经历三个阶段:
- 捕获阶段(Capture Phase)
- 从
window
→ 目标元素的 父级链(自上而下) - 默认情况下,事件监听器 不会在此阶段触发
- 从
- 目标阶段(Target Phase)
- 到达实际触发事件的元素
- 冒泡阶段(Bubble Phase)
- 从目标元素 →
window
(自下而上) - 大多数事件监听器在此阶段触发
- 从目标元素 →
2. 捕获模式的核心特点
(1)触发顺序与冒泡相反
<div id="grand">
<div id="parent">
<button id="child">点击</button>
</div>
</div>
捕获模式 事件传播顺序:
- 捕获阶段:
window
→#grand
→#parent
(自上而下) - 目标阶段:
#child
- 冒泡阶段:
#child
→#parent
→#grand
(自下而上)
(2)需要显式启用
默认情况下,事件监听器在 冒泡阶段 触发。要启用捕获阶段监听,需明确指定:
// 原生 JS
element.addEventListener('click', handler, true); // 第三个参数 true 表示捕获阶段
// Vue 中的等效写法
<div @click.capture="handler"></div>
3. Vue 中的捕获模式
通过 .capture
修饰符实现:
<div @click.capture="handleCapture">
<button @click="handleClick">点击</button>
</div>
执行顺序:
- 父元素的
handleCapture
(捕获阶段) - 子元素的
handleClick
(目标阶段 + 冒泡阶段)
4. 典型应用场景
(1)优先拦截事件
<div @click.capture="checkPermission">
<!-- 只有权限验证通过后,子元素事件才会执行 -->
<button @click="submitData">提交</button>
</div>
(2)避免冒泡被阻止
如果子元素调用了 event.stopPropagation()
,冒泡阶段的事件会被阻止,但 捕获阶段的事件仍会执行:
// 子组件
methods: {
handleClick() {
this.$emit('click');
event.stopPropagation(); // 阻止冒泡,但不影响捕获阶段
}
}
(3)性能优化
对大型列表使用事件委托时,捕获阶段可更早处理事件:
<ul @click.capture="handleItemClick">
<li v-for="item in 1000" :key="item.id">{{ item.text }}</li>
</ul>
5. 对比捕获与冒泡
特性 | 捕获阶段 | 冒泡阶段 |
---|---|---|
方向 | 顶层 → 目标元素(自上而下) | 目标元素 → 顶层(自下而上) |
默认触发 | 需显式启用 | 是 |
Vue 语法 | `@event.capture` | @event |
阻止传播的影响 | stopPropagation() 不影响 |
会阻止后续冒泡 |
6. 完整事件流示例
<div @click.capture="log('祖父捕获')">
<div @click="log('父冒泡')" @click.capture="log('父捕获')">
<button @click="log('目标')">点击</button>
</div>
</div>
输出顺序:
- 祖父捕获(捕获阶段)
- 父捕获(捕获阶段)
- 目标(目标阶段)
- 父冒泡(冒泡阶段)
7. 注意事项
- 谨慎使用捕获:可能打乱预期的事件顺序
- 避免过度拦截:在捕获阶段阻止传播会影响所有子元素
- 组件自定义事件:Vue 的
$emit
事件 没有捕获阶段(仅限原生 DOM 事件)
理解捕获模式能帮助你更精细地控制事件流,尤其在需要 提前拦截事件 或 处理动态组件 时非常有用。