React ---基础知识【合成事件、受控/非受控组件】

203 阅读6分钟

基础知识

JSX

简称:Javascript and xml

React合成事件

合成事件的定义

React基于浏览器的事件机制自身实现了一套事件机制,包括事件注册、事件合成、事件冒泡、事件派发等,在React中这套事件机制被称为合成事件

合成事件机制【juejin.cn/post/750042…

createRoot创建根节点时,通过事件委托来监听所有事件,后续在通过分发来触发我们代码中的回调,整个事件的流程,React 使用插件机制来实现:

  • 1、事件的注册 React 在初始化时,所有的事件注册在引入DOMPluginEventSystem.js全局执行: React 在注册时会将事件分为下面三种情况,通过createEventListenerWrapperWithPriority分配不同调度策略:
    • 离散事件:立即执行,抢占式调度(如click)
    • 用户阻塞事件:微任务队列调度,避免阻塞渲染(如drag)
    • 连续事件:宏任务队列调度,保证流畅性(如scroll)
  • 2、顶层事件监听和分发 React在应用根容器上同意绑定事件监听:
    • 遍历allNativeEvents,为每个事件类型绑定捕获/冒泡阶段的监听器;
    • 事件代理函数dispatchEvent作为统一回调,通过事件冒泡/捕获路径触发插件处理逻辑。
  • 3、事件的提取 当原生事件触发时,React通过插件分层处理机制生成合成事件:
    • 事件分发入口dispatchEventForPluginEventSystem核心是调用dispatchEventsForPlugins,进而触发插件的extractEvents()方法收集所有事件回调,processDispatchQueue执行所有回调
    • 递归Fiber树收集处理函数extractEvents()会沿组件树向上遍历,收集所有绑定的事件回调;
    • 合成事件对象构造:插件对有回调函数的创建SyntheticEvent实例,封装原生事件属性和跨浏览器兼容逻辑。
  • 4、事件的分发 最终都会通过processDispatchQueueItemsInOrder安装顺序来执行。

所有的原生事件都有对应的合成事件吗?

并不是,比如audio,video标签的媒体事件,是document所不具有的

执行顺序

先执行原生事件,后执行合成事件。合成事件会冒泡到document上,

React 的合成事件机制通过事件冒泡和事件委托来实现。当在组件中触发事件时,React 会将该事件包装成一个合成事件对象,并在组件树中冒泡传递,直到根节点处。在组件树中,React 使用事件委托机制将事件处理程序绑定到根节点上,统一处理所有组件的事件。

合成事件的特点

  • 1、浏览器兼容性

不同的浏览器对事件的实现存在差异,React将底层浏览器事件封装成统一的合成事件对象(fiber对象),提供一种“顶层注册、事件收集、统一触发”的事件机制。抹平了不同浏览器事件对象之间的差异,将不同平台事件模拟为合成事件。

  • 2. 性能优化

React的合成事件系统通过事件委托的方式,将所有事件都绑定在顶层组件(如document或挂载的容器)上进行统一处理。这种方式减少了内存的使用,因为不需要在每个DOM元素上都注册事件监听器。同时,React还引入了事件池机制,事件对象不会被频繁创建和销毁,而是存放进一个数组中,当事件触发时,就从这个数组中弹出事件对象,从而避免了垃圾回收的压力,提高了应用的性能。

    1. 简化事件处理逻辑

React的合成事件系统抽象了原生事件的细节,如事件捕获和冒泡阶段等,开发者只需要关注事件的冒泡阶段。这使得事件处理逻辑更加简洁明了,减少了不必要的代码复杂度。同时,合成事件对象提供了与原生事件相似的API,涵盖了大部分常用的属性和方法,如event.type、event.target、event.preventDefault()等,使得开发者可以更容易地处理用户交互。

    1. 提供一致的开发体验

React的合成事件系统与React的其他特性(如虚拟DOM、组件状态管理等)紧密集成,提供了一致的开发体验。这使得开发者可以更加专注于业务逻辑的实现,而不必花费大量时间在处理浏览器兼容性问题和优化性能上。

    1. 安全性和可维护性

React的合成事件系统提供了一个安全的环境,可以防止一些常见的安全问题,如跨站脚本攻击(XSS)。同时,它也使得开发者可以更容易地控制事件的行为。此外,由于合成事件对象在事件处理函数调用后被回收,这有助于避免内存泄漏问题。而且,事件处理函数可以在组件卸载时自动清理,进一步提高了应用的可维护性。

受控组件、非受控组件

受控组件:state完全受prop控制的组件

受控组件的值由React状态(或属性)进行管理。需要在组件内部定义状态,并通过事件处理函数更新状态

const [inputvalue,setInputValue]= useState("")
<input type="text" 
   value={inputvalue} //受控
   onChange={(e)=>setInputValue(e.target.value)} //更新state
/>

受控组件更新state流程:

  • 通过初始state声明数据,通过value属性绑定为表单的默认值
  • 表单值发生变化时,调用onChange事件处理器
  • 通过事件对象event拿到改变后的状态,并更新组件的state
  • 一旦setState方法更新state,重新渲染视图,表单组件的更新

非受控组件

非受控组件的值不受react状态的控制,而是直接绑定到DOM元素

  • 非受控组件:组件存在state,且state仅保存在这个组件的内部,受组件内部的setState改变,不受props影响
import {useRef} from 'react''

const inputRef = useRef(null)
const handleSubmit =()=>{
  alert(`输入值:${inputRef.current.value}`)
}

<input type="text" ref={inputRef}/> //{/* 非受控 */}
<button onClick={handleSubmit}>提交</button>

受控组件与非受控组件的区别(组件的值是由react状态控制还是由DOM节点控制)

  • 受控组件:
    • 受控组件依赖于状态
    • 受控组件只有继承React.Component才会有状态
    • 受控组件必须要在表单上使用onChange事件绑定对应的事件
    • 受控组件的修改会实时映射到状态值上,此时可以对输入的内容进行校验
  • 非受控组件
    • 非受控组件不受状态的控制
    • 非受控组件获取数据就是相当于操作DOM,不会更新react状态