事件委托和事件池
 
 
React 的合成事件是通过事件委托(event delegation)和事件池(event pooling)的机制来实现的。下面是合成事件的简单原理介绍:
 
1、 事件委托:React 使用事件委托将事件处理函数附加到父元素上,而不是直接将事件处理函数附加到每个子元素上。这意味着只需要一个事件监听器来处理整个组件树中的所有事件。当事件发生时,事件会冒泡到父元素,然后由 React 在父元素上触发相应的事件处理函数。
 
2、 事件池:React 使用事件池来重用事件对象,以减少内存分配和垃圾回收的开销。当事件发生时,React 会从事件池中获取一个事件对象,并将相关的信息(如事件类型、目标元素等)填充到事件对象中。然后,事件对象会被传递给事件处理函数。在事件处理完成后,React 会将事件对象重置,并放回事件池中,以便下次重用。
 
合成事件的原理带来了一些优势:
 
1、 跨浏览器兼容性:React 的合成事件封装了底层浏览器差异,使得事件在不同浏览器中表现一致,无需开发者自己处理兼容性问题。
 
2、 性能优化:通过事件委托,React 可以减少事件监听器的数量,从而提高性能。而且,事件池的机制可以减少内存分配和垃圾回收的开销,提升整体性能。
 
3、 事件代理:事件委托使得 React 可以在父组件上捕获事件,并根据事件的目标元素来触发相应的事件处理函数。这样可以更灵活地处理事件,而不需要为每个子元素都添加事件监听器。
 
4、 合成事件属性:React 的合成事件对象提供了一些额外的属性,例如 event.target、event.currentTarget、event.preventDefault() 等,方便开发者对事件进行处理和控制。
 
总结起来,React 的合成事件通过事件委托和事件池的机制,提供了跨浏览器兼容性、性能优化、事件代理和额外的事件属性等优势,使得事件处理更加方便和高效。
事件池
合成事件 SyntheticEvent 对象会被放入池中统一管理。这意味着 SyntheticEvent 对象可以被复用,当所有事件处理函数被调用之后,其所有属性都会被置空。
 
例如,以下代码是无效的:
function handleChange(e) {// This won't work because the event object gets reused.setTimeout(() => {console.log(e.target.value); // Too late!}, 100);}
 
如果你需要在事件处理函数运行之后获取事件对象的属性,你需要调用 e.persist():
function handleChange(e) {// Prevents React from resetting its properties:e.persist();
setTimeout(() => {console.log(e.target.value); // Works}, 100);}
 
以下是一个简单的代码演示,展示如何使用事件池:
var eventPool = {events: [],
getEvent: function(eventType) {if (this.events.length > 0) {return this.events.pop();} else {return { type: eventType };}},
releaseEvent: function(event) {event.target = null;event.currentTarget = null;this.events.push(event);}};
// 使用事件池对象获取和释放事件var eventType = 'click';var event = eventPool.getEvent(eventType);
// 模拟事件处理console.log('处理事件:', event);
// 释放事件到事件池eventPool.releaseEvent(event);
 
实际上,React 17 不再使用事件池的概念。React 17 引入了一个新的事件系统,称为"无池事件系统"(No Pooling Event System)。
 
在之前的 React 版本中,事件池的目的是为了重用事件对象,以减少内存分配和垃圾回收的开销。然而,随着现代浏览器的发展和性能改进,以及对 React 内部事件系统的优化,事件池的性能优势逐渐减弱。
 
React 17 的无池事件系统采用了一种新的事件处理机制,它不再重用事件对象,而是在每次事件触发时创建一个新的事件对象。这样做的主要原因是,现代浏览器在处理大量短暂的事件对象时,已经具备了很好的性能优化能力,不再需要手动管理事件对象的重用。
 
<button id="myButton">点击我</button>
<script>function simulateSyntheticEvent(element, eventType) {var event = new CustomEvent(eventType, {bubbles: true,cancelable: true,});element.dispatchEvent(event);}
function handleClick(event) {console.log('点击事件触发');}
var button = document.getElementById('myButton');button.addEventListener('click', handleClick);
// 模拟点击事件simulateSyntheticEvent(button, 'click');</script>
 
通过移除事件池,React 17 在事件处理方面获得了一些优势:
1、 更好的跨浏览器兼容性:不再需要担心不同浏览器对事件池的支持和行为差异。
2、 更简化的代码逻辑:无需关注事件对象的释放和重用,减少了开发者需要处理的细节。
3、 更好的可维护性和可扩展性:去除事件池使 React 内部的事件系统更加简洁和灵活,有助于未来的优化和改进。
 
需要注意的是,尽管 React 17 不再使用事件池,但它仍然提供了一套强大的合成事件机制,使开发者可以方便地处理和管理事件。你可以像之前一样,在 React 组件中定义事件处理函数,并将其传递给相应的事件属性,React 将负责处理事件的创建和传递。
 
综上所述,React 17 不再使用事件池的原因是为了简化代码逻辑、提供更好的跨浏览器兼容性,并利用现代浏览器的性能优化能力。
