Maison > interface Web > Questions et réponses frontales > Pourquoi React utilise-t-il des événements synthétiques ?

Pourquoi React utilise-t-il des événements synthétiques ?

青灯夜游
Libérer: 2022-12-15 19:24:06
original
3052 Les gens l'ont consulté

React utilise des événements synthétiques à trois fins principales : 1. Pour assurer la compatibilité du navigateur et obtenir une meilleure multiplateforme ; les événements synthétiques fournis par React peuvent être utilisés pour atténuer les différences entre les différents objets d'événement du navigateur et simuler et synthétiser des événements provenant de différents événement de plateformes. 2. Évitez le garbage collection ; l'objet d'événement React ne sera pas libéré, mais sera stocké dans un tableau lorsque l'événement est déclenché, il sera retiré du tableau pour éviter une création et une destruction fréquentes (garbage collection). 3. Faciliter la gestion unifiée des événements et le mécanisme de transaction.

Pourquoi React utilise-t-il des événements synthétiques ?

L'environnement d'exploitation de ce tutoriel : système Windows 7, version React18, ordinateur Dell G3.

1. Qu'est-ce qu'un événement synthétique ?

L'événement synthétique React (SyntheticEvent) est un objet événement qui React simule toutes les capacités des événements DOM natifs, c'est-à-dire un wrapper multi-navigateur pour les événements natifs du navigateur. Il définit des événements synthétiques selon la spécification W3C, est compatible avec tous les navigateurs et possède la même interface que les événements natifs des navigateurs.

Dans React, tous les événements sont des événements DOM synthétiques et non natifs, mais les événements DOM peuvent être obtenus via la propriété e.nativeEvent. Par exemple :

const button = <button>react 按钮</button>
const handleClick = (e) => console.log(e.nativeEvent); //原生事件对象
Copier après la connexion

Lorsque vous apprenez une nouvelle connaissance, vous devez savoir pourquoi cette technologie apparaît.

Alors pourquoi React utilise-t-il des événements synthétiques ? Il a trois objectifs principaux :

  • Activer la compatibilité des navigateurs et obtenir une meilleure multiplateforme

    React utilise un mécanisme de proxy d'événement de niveau supérieur, qui peut garantir la cohérence du bouillonnement et peut être exécuté sur tous les navigateurs. Les événements synthétiques fournis par React sont utilisés pour atténuer les différences entre les différents objets d'événement du navigateur et simuler des événements synthétiques provenant de différents événements de plate-forme.

  • Évitez la collecte des ordures

    Les objets d'événement peuvent être fréquemment créés et recyclés, c'est pourquoi React introduit un pool d'événements pour acquérir ou libérer des objets d'événement dans le pool d'événements. Autrement dit, l'objet événement React ne sera pas publié, mais sera stocké dans un tableau lorsque l'événement est déclenché, il sera retiré du tableau pour éviter une création et une destruction fréquentes (garbage collection).

  • Gestion d'événements et mécanisme de transaction unifiés pratiques

本文不介绍源码啦,对具体实现的源码有兴趣的朋友可以查阅:《React SyntheticEvent》 。
https://github.com/facebook/react/blob/75ab53b9e1de662121e68dabb010655943d28d11/packages/events/SyntheticEvent.js#L62
Copier après la connexion

2. Examen des événements natifs

Les modèles d'événements JavaScript sont principalement divisés en 3 types : modèle d'événement original (DOM0), modèle d'événement DOM2, événement IE modèle .

1.DOM0 event model

est également appelé modèle d'événement original Dans ce modèle, les événements ne se propagent pas, c'est-à-dire qu'il n'y a pas de concept de flux d'événements. La fonction d'écoute de liaison d'événement est relativement simple, il existe deux manières :

//HTML代码种直接绑定:
<button type=&#39;button&#39; id="test" onclick="fun()"/>

//通过JS代码指定属性值:
var btn = document.getElementById(&#39;.test&#39;);
btn.onclick = fun;
//移除监听函数:
btn.onclick = null;
Copier après la connexion

Avantages : Forte compatibilité prend en charge tous les navigateurs

Inconvénients : Il n'y a pas de séparation entre la logique et l'affichage, une seule fonction d'écoute pour le même événement peut être liée, et la fonction d'écoute de liaison d'événement est relativement simple. le même enregistré plus tard. Cet événement écrasera ceux enregistrés précédemment.

Modèle d'événement 2.DOM2

Le modèle standard établi par le W3C, les navigateurs modernes (navigateurs sauf IE6-8) prennent en charge ce modèle. Dans ce modèle d'événement, il existe trois processus pour un événement :

Phase de capture d'événement (phase de capture). L'événement se propage du document jusqu'à l'élément cible, vérifie si les nœuds passants sont liés à la fonction d'écoute d'événement et l'exécute si tel est le cas.

Phase de traitement des événements (phase cible). Lorsque l'événement atteint l'élément cible, la fonction d'écoute de l'élément cible est déclenchée.

Phase bouillonnante de l'événement. L'événement bouillonne de l'élément cible vers le document, vérifie si les nœuds passants sont liés à la fonction d'écoute d'événement et l'exécute si tel est le cas.

//事件绑定监听函数的方式如下:
addEventListener(eventType, handler, useCapture)

//事件移除监听函数的方式如下:
removeEventListener(eventType, handler, useCapture)
Copier après la connexion

3.Modèle d'événement IE

Le modèle d'événement IE comporte deux processus :

phase de traitement de l'événement (phase cible). Lorsque l'événement atteint l'élément cible, la fonction d'écoute de l'élément cible est déclenchée.

Phase bouillonnante de l'événement. L'événement bouillonne de l'élément cible vers le document, vérifie si les nœuds passants sont liés à la fonction d'écoute d'événement et l'exécute si tel est le cas.

//事件绑定监听函数的方式如下:
attachEvent(eventType, handler)

//事件移除监听函数的方式如下:
detachEvent(eventType, handler)
Copier après la connexion

4. Flux d'événements

Pourquoi React utilise-t-il des événements synthétiques ?

Comme le montre la figure ci-dessus, ce que l'on appelle le flux d'événements comprend trois étapes : la capture d'événement, l'étape cible et le bouillonnement d'événement. La capture d'événement s'effectue de l'extérieur vers l'intérieur, correspondant à la flèche rouge marquée fenêtre -> document html -> cible dans la figure. L'étape cible est l'étape où l'événement se produit réellement et est traité. . Le bouillonnement de l'événement s'effectue de l'intérieur vers l'extérieur. , correspondant à la fenêtre cible -> ->

  • Capture d'événement

    Lorsqu'un élément déclenche un événement (comme onclick), le document objet de niveau supérieur émettra un flux d'événements, circulant vers le nœud de l'élément cible avec les nœuds de l'arborescence DOM jusqu'à ce qu'il atteigne le point où l’événement se produit réellement. Durant ce processus, la fonction d'écoute correspondante de l'événement ne sera pas déclenchée.

  • Event target

    Après avoir atteint l'élément cible, exécutez la fonction de traitement correspondante de l'événement de l'élément cible. Si aucune fonction d'écoute n'est liée, elle ne sera pas exécutée.

  • Événement bouillonnant

    从目标元素开始,往顶层元素传播。途中如果有节点绑定了相应的事件处理函数,这些函数都会被触发一次。如果想阻止事件起泡,可以使用 e.stopPropagation() 或者 e.cancelBubble=true(IE)来阻止事件的冒泡传播。

  • 事件委托/事件代理

    简单理解就是将一个响应事件委托到另一个元素。 当子节点被点击时,click 事件向上冒泡,父节点捕获到事件后,我们判断是否为所需的节点,然后进行处理。其优点在于减少内存消耗和动态绑定事件。

三、React合成事件原理

React合成事件的工作原理大致可以分为两个阶段:

  • 事件绑定

  • 事件触发

在React17之前,React是把事件委托在document上的,React17及以后版本不再把事件委托在document上,而是委托在挂载的容器上了,本文以16.x版本的React为例来探寻React的合成事件。当真实的dom触发事件时,此时构造React合成事件对象,按照冒泡或者捕获的路径去收集真正的事件处理函数,在此过程中会先处理原生事件,然后当冒泡到document对象后,再处理React事件。举个栗子:

import React from &#39;react&#39;;
import &#39;./App.less&#39;;

class Test extends React.Component {
  parentRef: React.RefObject<any>;

  childRef: React.RefObject<any>;

  constructor(props) {
    super(props);
    this.parentRef = React.createRef();
    this.childRef = React.createRef();
  }

  componentDidMount() {
    document.addEventListener(
      &#39;click&#39;,
      () => {
        console.log(`document原生事件捕获`);
      },
      true,
    );
    document.addEventListener(&#39;click&#39;, () => {
      console.log(`document原生事件冒泡`);
    });
    this.parentRef.current.addEventListener(
      &#39;click&#39;,
      () => {
        console.log(`父元素原生事件捕获`);
      },
      true,
    );
    this.parentRef.current.addEventListener(&#39;click&#39;, () => {
      console.log(`父元素原生事件冒泡`);
    });
    this.childRef.current.addEventListener(
      &#39;click&#39;,
      () => {
        console.log(`子元素原生事件捕获`);
      },
      true,
    );
    this.childRef.current.addEventListener(&#39;click&#39;, () => {
      console.log(`子元素原生事件冒泡`);
    });
  }

  handleParentBubble = () => {
    console.log(`父元素React事件冒泡`);
  };

  handleChildBubble = () => {
    console.log(`子元素React事件冒泡`);
  };

  handleParentCapture = () => {
    console.log(`父元素React事件捕获`);
  };

  handleChileCapture = () => {
    console.log(`子元素React事件捕获`);
  };

  render() {
    return (
      <p
        ref={this.parentRef}
        onClick={this.handleParentBubble}
        onClickCapture={this.handleParentCapture}
      >
        <p
          ref={this.childRef}
          onClick={this.handleChildBubble}
          onClickCapture={this.handleChileCapture}
        >
          事件处理测试
        </p>
      </p>
    );
  }
}

export default Test;
Copier après la connexion

上面案例打印的结果为:

Pourquoi React utilise-t-il des événements synthétiques ?

注:React17中上述案例的执行会有所区别,会先执行所有捕获事件后,再执行所有冒泡事件。

1、事件绑定

通过上述案例,我们知道了React合成事件和原生事件执行的过程,两者其实是通过一个叫事件插件(EventPlugin)的模块产生关联的,每个插件只处理对应的合成事件,比如onClick事件对应SimpleEventPlugin插件,这样React在一开始会把这些插件加载进来,通过插件初始化一些全局对象,比如其中有一个对象是registrationNameDependencies,它定义了合成事件与原生事件的对应关系如下:

{
    onClick: [&#39;click&#39;],
    onClickCapture: [&#39;click&#39;],
    onChange: [&#39;blur&#39;, &#39;change&#39;, &#39;click&#39;, &#39;focus&#39;, &#39;input&#39;, &#39;keydown&#39;, &#39;keyup&#39;, &#39;selectionchange&#39;],
    ...
}
Copier après la connexion

registrationNameModule对象指定了React事件到对应插件plugin的映射:

{
    onClick: SimpleEventPlugin,
    onClickCapture: SimpleEventPlugin,
    onChange: ChangeEventPlugin,
    ...
}
Copier après la connexion

plugins对象就是上述插件的列表。在某个节点渲染过程中,合成事件比如onClick是作为它的prop的,如果判断该prop为事件类型,根据合成事件类型找到对应依赖的原生事件注册绑定到顶层document上,dispatchEvent为统一的事件处理函数。

2、事件触发

当任意事件触发都会执行dispatchEvent函数,比如上述事例中,当用户点击Child的p时会遍历这个元素的所有父元素,依次对每一级元素进行事件的收集处理,构造合成事件对象(SyntheticEvent–也就是通常我们说的React中自定义函数的默认参数event,原生的事件对象对应它的一个属性),然后由此形成了一条「链」,这条链会将合成事件依次存入eventQueue中,而后会遍历eventQueue模拟一遍捕获和冒泡阶段,然后通过runEventsInBatch方法依次触发调用每一项的监听事件,在此过程中会根据事件类型判断属于冒泡阶段还是捕获阶段触发,比如onClick是在冒泡阶段触发,onClickCapture是在捕获阶段触发,在事件处理完成后进行释放。SyntheticEvent对象属性如下:

boolean bubbles
boolean cancelable
DOMEventTarget currentTarget
boolean defaultPrevented
number eventPhase
boolean isTrusted
DOMEvent nativeEvent // 原生事件对象
void preventDefault()
boolean isDefaultPrevented()
void stopPropagation()
boolean isPropagationStopped()
void persist()
DOMEventTarget target
number timeStamp
string type
Copier après la connexion

dispatchEvent伪代码如下:

dispatchEvent = (event) => {
    const path = []; // 合成事件链
    let current = event.target; // 触发事件源
    while (current) {
      path.push(current);
      current = current.parentNode; // 逐级往上进行收集
    }
    // 模拟捕获和冒泡阶段
    // path = [target, p, body, html, ...]
    for (let i = path.length - 1; i >= 0; i--) {
      const targetHandler = path[i].onClickCapture;
      targetHandler && targetHandler();
    }
    for (let i = 0; i < path.length; i++) {
      const targetHandler = path[i].onClick;
      targetHandler && targetHandler();
    }
  };
Copier après la connexion

3、更改事件委托(React v17.0)

自React发布以来, 一直自动进行事件委托。当 document 上触发 DOM 事件时,React 会找出调用的组件,然后 React 事件会在组件中向上 “冒泡”。但实际上,原生事件已经冒泡出了 document 级别,React 在其中安装了事件处理器。

但是,这就是逐步升级的困难所在。

在 React 17 中,React 将不再向 document 附加事件处理器。而会将事件处理器附加到渲染 React 树的根 DOM 容器中:

const rootNode = document.getElementById(&#39;root&#39;);
ReactDOM.render(<App />, rootNode);
Copier après la connexion

在 React 16 或更早版本中,React 会对大多数事件执行 document.addEventListener()。React 17 将会在底层调用 rootNode.addEventListener()

Pourquoi React utilise-t-il des événements synthétiques ?

四、注意

由于事件对象可能会频繁创建和回收在React16.x中,合成事件SyntheticEvent采用了事件池,合成事件会被放进事件池中统一管理,这样能够减少内存开销。React通过合成事件,模拟捕获和冒泡阶段,从而达到不同浏览器兼容的目的。另外,React不建议将原生事件和合成事件一起使用,这样很容易造成使用混乱。

En raison des changements dans la délégation des événements dans la version 17, l'imbrication des anciens et des nouveaux arbres React est désormais plus sûre. Notez que pour que cela fonctionne, les deux versions doivent être 17 ou supérieures, c'est pourquoi la mise à niveau vers React 17 et supérieure est fortement recommandée.

【Recommandation associée : Tutoriel vidéo Redis

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal