React中的事件-合成事件与原生事件

在 React 应用开发中,事件处理是实现交互的基础。React 通过合成事件(Synthetic Events)和原生事件(Native Events)提供了一套强大的事件处理机制。理解这两种事件的区别和适用场景,对于编写高效、可维护的 React 应用至关重要。

合成事件 (Synthetic Events)

合成事件是 React 为了保证跨浏览器兼容性和性能优化而创建的一种事件系统。它们模拟了原生事件,但提供了更一致的接口和行为。合成事件在 React 的虚拟 DOM 层上处理,允许 React 在事件处理和更新中进行优化。

特点:

  1. 跨浏览器兼容性:合成事件屏蔽了不同浏览器之间的差异,提供了统一的接口。
  2. 批处理:在合成事件中,setState 调用是异步的,React 会将多次状态更新合并为一次重新渲染。
  3. 事件委托:合成事件使用事件委托机制,将所有事件处理器绑定到根元素上,从而减少事件处理器的数量,提高性能。

常见合成事件:

  • 鼠标事件:onClick, onDoubleClick, onMouseDown, onMouseUp, onMouseMove, onMouseEnter, onMouseLeave
  • 表单事件:onChange, onInput, onSubmit, onFocus, onBlur
  • 键盘事件:onKeyDown, onKeyPress, onKeyUp
  • 触摸事件:onTouchStart, onTouchMove, onTouchEnd
  • 滚动事件:onScroll
  • 拖拽事件:onDrag, onDragStart, onDragEnd, onDragOver, onDragEnter, onDragLeave

原生事件 (Native Events)

原生事件是浏览器原生提供的事件系统,直接绑定到 DOM 元素上。它们没有经过 React 的处理和优化,事件处理器直接响应浏览器的事件。

特点:

  1. 直接绑定:原生事件处理器直接绑定到 DOM 元素上。
  2. 同步更新:在原生事件中,setState 调用是同步的,每次状态更新都会立即触发重新渲染。
  3. 无统一接口:原生事件的接口和行为可能在不同浏览器中有所不同。

常见原生事件:

  • 使用 addEventListener 绑定的所有事件,如:click, dblclick, mousedown, mouseup, mousemove, mouseenter, mouseleave, change, input, submit, focus, blur, keydown, keypress, keyup, touchstart, touchmove, touchend, scroll, drag, dragstart, dragend, dragover, dragenter, dragleave

示例代码

合成事件:

import React, { useState } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  const handleClick = () => {
    setCount(count + 1);
    console.log('Synthetic event count:', count);
  };

  return (
    <button onClick={handleClick}>Click me</button>
  );
}

export default Example;
import React, { useState } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  const handleClick = () => {
    setCount(count + 1);
    console.log('Synthetic event count:', count);
  };

  return (
    <button onClick={handleClick}>Click me</button>
  );
}

export default Example;

原生事件:

import React, { useEffect, useState } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const handleClick = () => {
      setCount(count + 1);
      console.log('Native event count:', count);
    };

    document.getElementById('nativeButton').addEventListener('click', handleClick);

    return () => {
      document.getElementById('nativeButton').removeEventListener('click', handleClick);
    };
  }, [count]);

  return (
    <button id="nativeButton">Click me</button>
  );
}

export default Example;
import React, { useEffect, useState } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const handleClick = () => {
      setCount(count + 1);
      console.log('Native event count:', count);
    };

    document.getElementById('nativeButton').addEventListener('click', handleClick);

    return () => {
      document.getElementById('nativeButton').removeEventListener('click', handleClick);
    };
  }, [count]);

  return (
    <button id="nativeButton">Click me</button>
  );
}

export default Example;

在这个例子中:

  • 合成事件中,setState 是异步的,所以 console.log 会输出旧的 count 值。
  • 原生事件中,setState 是同步的,所以 console.log 会立即反映最新的 count 值。

在 React 18 中自动批处理以减少渲染