事件委托和事件池

前端精髓

共 4710字,需浏览 10分钟

 · 2024-04-10

38f18dc58c301fe3e210cc3ea6703300.webp


React 的合成事件是通过事件委托(event delegation)和事件池(event pooling)的机制来实现的。下面是合成事件的简单原理介绍:


1、 事件委托:React 使用事件委托将事件处理函数附加到父元素上,而不是直接将事件处理函数附加到每个子元素上。这意味着只需要一个事件监听器来处理整个组件树中的所有事件。当事件发生时,事件会冒泡到父元素,然后由 React 在父元素上触发相应的事件处理函数。


2、 事件池:React 使用事件池来重用事件对象,以减少内存分配和垃圾回收的开销。当事件发生时,React 会从事件池中获取一个事件对象,并将相关的信息(如事件类型、目标元素等)填充到事件对象中。然后,事件对象会被传递给事件处理函数。在事件处理完成后,React 会将事件对象重置,并放回事件池中,以便下次重用。


合成事件的原理带来了一些优势:


1、 跨浏览器兼容性:React 的合成事件封装了底层浏览器差异,使得事件在不同浏览器中表现一致,无需开发者自己处理兼容性问题。


2、 性能优化:通过事件委托,React 可以减少事件监听器的数量,从而提高性能。而且,事件池的机制可以减少内存分配和垃圾回收的开销,提升整体性能。


3、 事件代理:事件委托使得 React 可以在父组件上捕获事件,并根据事件的目标元素来触发相应的事件处理函数。这样可以更灵活地处理事件,而不需要为每个子元素都添加事件监听器。


4、 合成事件属性:React 的合成事件对象提供了一些额外的属性,例如 event.targetevent.currentTargetevent.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 不再使用事件池的原因是为了简化代码逻辑、提供更好的跨浏览器兼容性,并利用现代浏览器的性能优化能力。

浏览 3
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报