React事件处理机制

439 阅读4分钟

前言✨

在了解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 合成。