基础知识
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还引入了事件池机制,事件对象不会被频繁创建和销毁,而是存放进一个数组中,当事件触发时,就从这个数组中弹出事件对象,从而避免了垃圾回收的压力,提高了应用的性能。
-
- 简化事件处理逻辑
React的合成事件系统抽象了原生事件的细节,如事件捕获和冒泡阶段等,开发者只需要关注事件的冒泡阶段。这使得事件处理逻辑更加简洁明了,减少了不必要的代码复杂度。同时,合成事件对象提供了与原生事件相似的API,涵盖了大部分常用的属性和方法,如event.type、event.target、event.preventDefault()等,使得开发者可以更容易地处理用户交互。
-
- 提供一致的开发体验
React的合成事件系统与React的其他特性(如虚拟DOM、组件状态管理等)紧密集成,提供了一致的开发体验。这使得开发者可以更加专注于业务逻辑的实现,而不必花费大量时间在处理浏览器兼容性问题和优化性能上。
-
- 安全性和可维护性
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状态