js 事件机制
先回顾下 js 的事件机制
事件触发三阶段
- window 往事件触发处传播,遇到捕获事件会触发回调
- 传播到事件触发处时触发事件回调
- 从事件触发往 window 传播,遇到冒泡事件会触发回调
事件委托
在事件机制中,执行完目标事件回调后,事件会往父元素冒泡,最终直到根元素。事件委托其实就是依赖于事件冒泡实现的。事件委托相较于监听目标事件的优势在于 统一管理事件 和 节省内存开销
<ul id="ul">
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
// 事件委托小栗子
let ul = document.querySelector("#ul");
ul.addEventListener("click", (event) => {
console.log(event.target);
});
</script>
事件常用方法
e.stopPropagation
阻止事件捕获或冒泡e.preventDefault
阻止默认行为e.stopImmediatePropagation
阻止触发同一元素的其他相同事件
react18 事件机制
<div ref={
parentRef} onClick={
parentBubble} onClickCapture={
parentCapture}>
<button ref={
childRef} onClick={
childBubble} onClickCapture={
childCapture}>
点击
</button>
</div>
上面代码层级如下 document -> root -> div -> button
- document 注册的事件最先触发
- root 注册的事件触发,React 根据当前点击的 event.target 收集对应 DOM 节点的 fiber 节点中的 pendingProps,pendingProps 在这里简单理解就是 jsx 中 DOM 节点对应的 props,收集 props 中的 onClickCapture(因为触发的是点击事件,所以收集 onClickCapture 捕获函数),最终在队列中收集成 [childCapture, parentCapture],然后倒序触发
- div 注册的捕获事件触发
- p 注册的捕获事件触发
- p 注册的冒泡事件触发
- div 注册的冒泡事件触发
- root 收集的队列里有两个冒泡事件, [childBubble, parentBubble], 然后正序触发
- document 注册的冒泡事件触发
react 17 合成事件机制
其实 React 的事件机制就是一个典型的事件代理,将所有 DOM 节点的事件代理到根节点上统筹处理,基本原理如下:
- 初始化根节点,在根节点上绑定原生事件的监听器并分配优先级
- React 渲染过程中收集事件
- 渲染完成后调度真实事件的调用
源码执行过程如下:
- 执行
dispatchEvent
- 创建事件对应的合成事件
SyntheticEvent
- 收集捕获的回调函数和对应的节点实例
- 收集冒泡的回调函数和对应的节点实例
- 执行对应的回调函数,同时将
SyntheticEvent
作为参数传入
完整示例
比如我们在 react 中触发一次 click 事件
<div>
<button onClick={handerClick}>点击</button>
</div>
button 对应一个 Fiber Node,在 memoizedProps
上挂载事件
memoizedProps = {
onClick: handerClick,
}
react16 事件机制
1.事件注册
react
在解析jsx props
时会取出绑定的事件,通过事件的 map 对象找到原生事件类型,然后通过listenTo
方法将事件代理到document
上。listenTo
会将事件分为trapCapturedEvent
(捕获)和trapBubbledEvent
(