事件委托和事件池

React 的合成事件是通過事件委托(event delegation)和事件池(event pooling)的機制來實現(xiàn)的。下面是合成事件的簡單原理介紹:
1、 事件委托:React 使用事件委托將事件處理函數(shù)附加到父元素上,而不是直接將事件處理函數(shù)附加到每個子元素上。這意味著只需要一個事件監(jiān)聽器來處理整個組件樹中的所有事件。當事件發(fā)生時,事件會冒泡到父元素,然后由 React 在父元素上觸發(fā)相應的事件處理函數(shù)。
2、 事件池:React 使用事件池來重用事件對象,以減少內(nèi)存分配和垃圾回收的開銷。當事件發(fā)生時,React 會從事件池中獲取一個事件對象,并將相關(guān)的信息(如事件類型、目標元素等)填充到事件對象中。然后,事件對象會被傳遞給事件處理函數(shù)。在事件處理完成后,React 會將事件對象重置,并放回事件池中,以便下次重用。
合成事件的原理帶來了一些優(yōu)勢:
1、 跨瀏覽器兼容性:React 的合成事件封裝了底層瀏覽器差異,使得事件在不同瀏覽器中表現(xiàn)一致,無需開發(fā)者自己處理兼容性問題。
2、 性能優(yōu)化:通過事件委托,React 可以減少事件監(jiān)聽器的數(shù)量,從而提高性能。而且,事件池的機制可以減少內(nèi)存分配和垃圾回收的開銷,提升整體性能。
3、 事件代理:事件委托使得 React 可以在父組件上捕獲事件,并根據(jù)事件的目標元素來觸發(fā)相應的事件處理函數(shù)。這樣可以更靈活地處理事件,而不需要為每個子元素都添加事件監(jiān)聽器。
4、 合成事件屬性:React 的合成事件對象提供了一些額外的屬性,例如 event.target、event.currentTarget、event.preventDefault() 等,方便開發(fā)者對事件進行處理和控制。
總結(jié)起來,React 的合成事件通過事件委托和事件池的機制,提供了跨瀏覽器兼容性、性能優(yōu)化、事件代理和額外的事件屬性等優(yōu)勢,使得事件處理更加方便和高效。
事件池
合成事件 SyntheticEvent 對象會被放入池中統(tǒng)一管理。這意味著 SyntheticEvent 對象可以被復用,當所有事件處理函數(shù)被調(diào)用之后,其所有屬性都會被置空。
例如,以下代碼是無效的:
function handleChange(e) {// This won't work because the event object gets reused.setTimeout(() => {console.log(e.target.value); // Too late!}, 100);}
如果你需要在事件處理函數(shù)運行之后獲取事件對象的屬性,你需要調(diào)用 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 引入了一個新的事件系統(tǒng),稱為"無池事件系統(tǒng)"(No Pooling Event System)。
在之前的 React 版本中,事件池的目的是為了重用事件對象,以減少內(nèi)存分配和垃圾回收的開銷。然而,隨著現(xiàn)代瀏覽器的發(fā)展和性能改進,以及對 React 內(nèi)部事件系統(tǒng)的優(yōu)化,事件池的性能優(yōu)勢逐漸減弱。
React 17 的無池事件系統(tǒng)采用了一種新的事件處理機制,它不再重用事件對象,而是在每次事件觸發(fā)時創(chuàng)建一個新的事件對象。這樣做的主要原因是,現(xiàn)代瀏覽器在處理大量短暫的事件對象時,已經(jīng)具備了很好的性能優(yōu)化能力,不再需要手動管理事件對象的重用。
<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('點擊事件觸發(fā)');}
var button = document.getElementById('myButton');button.addEventListener('click', handleClick);
// 模擬點擊事件simulateSyntheticEvent(button, 'click');</script>
通過移除事件池,React 17 在事件處理方面獲得了一些優(yōu)勢:
1、 更好的跨瀏覽器兼容性:不再需要擔心不同瀏覽器對事件池的支持和行為差異。
2、 更簡化的代碼邏輯:無需關(guān)注事件對象的釋放和重用,減少了開發(fā)者需要處理的細節(jié)。
3、 更好的可維護性和可擴展性:去除事件池使 React 內(nèi)部的事件系統(tǒng)更加簡潔和靈活,有助于未來的優(yōu)化和改進。
需要注意的是,盡管 React 17 不再使用事件池,但它仍然提供了一套強大的合成事件機制,使開發(fā)者可以方便地處理和管理事件。你可以像之前一樣,在 React 組件中定義事件處理函數(shù),并將其傳遞給相應的事件屬性,React 將負責處理事件的創(chuàng)建和傳遞。
綜上所述,React 17 不再使用事件池的原因是為了簡化代碼邏輯、提供更好的跨瀏覽器兼容性,并利用現(xiàn)代瀏覽器的性能優(yōu)化能力。
