前言✨
在了解React事件机制之前,有必要了解一下DOM事件流。
一、DOM(文档对象模型)
是一个树型结构,当一个HTML元素产生一个事件时,该事件会在元素节点与根结点之间的路径传播,路径所经过的结点都会收到该事件,这个传播过程可称为DOM事件流。
二、DOM事件流阶段
在DOM兼容浏览器中,事件流分为3个阶段如下:
1.事件捕获阶段,事件从Document节点自上而下向目标节点传播的阶段。
路径:window —> document —> boy —> div—> p
2.目标阶段,具体元素已经捕获事件,真正的目标节点正在处理事件的阶段,如果阻止事件冒泡,那么该事件对象将在此阶段完成后停止传播。
3.冒泡阶段,事件从目标节点自下而上向Document节点传播的阶段。
路径:p—> div —> body —> document —> window
三、事件监听函数addEventListener()
element.addEventListener(event, function, useCapture)
event 事件名,如click
function 事件触发时执行的函数
useCapture:true 事件在捕获阶段执行,fase 默认值,事件在冒泡阶段执行
React事件处理
一、事件写法区别
传统的 HTML:
<button onclick="activeClick()">
点击
</button>
React中:
<button onClick={activeClick}>
点击
</button>
React 事件的命名采用小驼峰式(camelCase),而不是纯小写。 使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串。
二、阻止默认事件的写法区别
传统的 HTML:
<form onsubmit="console.log('You clicked submit.'); return false">
<button type="submit">Submit</button>
</form>
React中:
function Form() {
function handleSubmit(e) {
e.preventDefault();
console.log('You clicked submit.');
}
return (
<form onSubmit={handleSubmit}>
<button type="submit">Submit</button>
</form>
);
}
在 React 中另一个不同点是你不能通过返回 false 的方式阻止默认行为。你必须显式的使用 preventDefault,这里e 是一个合成事件,后面单独详解合成事件。
三、React合成事件(SyntheticEvent)
React 基于 Virtual DOM 实现了一个SyntheticEvent(合成事件)层,我们所定义的事件处理器会接收到一个SyntheticEvent对象的实例。与原生事件直接在元素上注册的方式不同的是,react的合成事件不会直接绑定到目标dom节点上,用事件委托机制,以队列的方式,从触发事件的组件向父组件回溯直到document节点,因此React组件上声明的事件最终绑定到了document 上。用一个统一的监听器去监听,这个监听器上保存着目标节点与事件对象的映射,当组件挂载或卸载时,只是在这个统一的事件监听器上插入或删除一些对象;当事件发生时,首先被这个统一的事件监听器处理,然后在映射里找到真正的事件处理函数并调用。这样做的好处:
1.抹平不同浏览器之间的兼容性差异。
2.事件"合成",即事件自定义。事件合成既可以处理兼容性问题,也可以用来自定义事件(例如 React 的 onChange 事件)。
3.提供一个抽象跨平台事件机制。类似 VirtualDOM 抽象了跨平台的渲染方式,合成事件(SyntheticEvent)提供一个抽象的跨平台事件机制。
4.可以做更多优化。例如利用事件委托机制,几乎所有事件的触发都代理到了 document,而不是 DOM 节点本身,简化了 DOM 事件处理逻辑,减少了内存开销。(React 自身模拟了一套事件冒泡的机制)
5.可以干预事件的分发。V16引入 Fiber 架构,React 可以通过干预事件的分发以优化用户的交互体验。
总结🎉
1.React v16 上注册的事件最终会绑定在document这个 DOM 上,而不是 React 组件对应的 DOM(减少内存开销就是因为所有的事件都绑定在 document 上,其他节点没有绑定事件)
2.React 自身实现了一套事件冒泡机制,所以这也就是为什么我们 event.stopPropagation()或者 return false 无效的原因,必须要地明确地调用preventDefault()来阻止默认行为
3.React 通过队列的形式,从触发的组件向父组件回溯,然后调用他们 JSX 中定义的 callback
4.React 有一套自己的合成事件 SyntheticEvent
5.命名方式:原生事件为全小写,React 事件采用小驼峰。事件函数处理语法:原生事件为字符串,React 事件为函数
拓展💻
React v17对事件处理机制进行了改动:
1.事件统一绑定#root上,而不是document上,这样好处是有利于微前端的,微前端一个前端系统中可能有多个应用,如果继续采取全部绑定在document上,那么可能多应用下会出现问题。
2.React 17 中终于支持了原生捕获事件的支持, 对齐了浏览器原生标准。同时 onScroll 事件不再进行事件冒泡。onFocus 和 onBlur 使用原生 focusin, focusout 合成。