React 合成事件和 JavaScript 事件有什么區(qū)別?
五一前幫一個同學做模擬面試的時候,聊到了 React 的合成事件和 JavaScript 原生事件的差異性問題。我發(fā)現(xiàn)很多 React 技術(shù)棧的同學對這一方面好像理解的并不清楚,所以今天咱們這篇文章主要就來說下這個問題。
1.設(shè)計理念
React合成事件 封裝 了原生瀏覽器事件,提供了統(tǒng)一的API接口,使得開發(fā)者無論瀏覽器環(huán)境如何都可以用相同的方式處理事件。這種方法的主要目的是:確??鐬g覽器的一致性并與 React 的聲明式編程模型和生命周期集成。
JavaScript 中的事件處理程序 由瀏覽器直接提供,與特定的瀏覽器行為密切相關(guān)。不同的瀏覽器可能有不同的實現(xiàn)和支持級別。
2.瀏覽器兼容性
React 合成事件處理所有瀏覽器兼容性問題,為開發(fā)人員提供一個干凈、一致的事件處理接口。
例如:它標準化了不同瀏覽器之間的事件行為,以事件冒泡和默認行為為例,咱們來看下它的實現(xiàn):
// 構(gòu)造器定義,接收事件配置、目標實例、本地事件和事件目標作為參數(shù)
function SyntheticEvent(dispatchConfig, targetInst, nativeEvent, nativeEventTarget) {
this.dispatchConfig = dispatchConfig; // 存儲事件的配置信息,例如事件類型
this._targetInst = targetInst; //存儲觸發(fā)事件的組件實例
this.nativeEvent = nativeEvent; // 存儲本機瀏覽器事件對象
this.target = nativeEventTarget; // 存儲事件的原生目標,即事件最初發(fā)生的多姆元素
this.currentTarget = null; // 處理事件的當前多姆元素將在事件處理期間設(shè)置
}
// SyneticEvents的原型對象,定義所有實例共享的方法
SyntheticEvent.prototype = {
// Method to prevent the default event behavior
preventDefault: function() {
this.defaultPrevented = true; // 標志著事件的默認行為已被阻止
const event = this.nativeEvent; // 獲取本地事件對象
if (!event) {
return; // 如果沒有本地事件對象,則直接返回
}
// 如果本機事件對象支持preventAlert方法,請調(diào)用它以防止默認行為
if (event.preventDefault) {
event.preventDefault();
} else if (typeof event.returnValue !== 'undefined') { // 與IE8及以下版本兼容
event.returnValue = false; // 在較舊的IE瀏覽器中,使用returnValue= true來防止默認行為
}
},
// 阻止事件進一步傳播的方法
stopPropagation: function() {
const event = this.nativeEvent; // 獲取本地事件對象
if (!event) {
return; // 如果沒有本地事件對象,則直接返回
}
// 如果本機事件對象支持stopPropagation方法,請調(diào)用它來停止事件冒泡
if (event.stopPropagation) {
event.stopPropagation();
} else if (typeof event.cancelBubble !== 'undefined') { // 與IE8及以下版本兼容
event.cancelBubble = true; // 在較舊的IE瀏覽器中,使用cancelBubble=true來停止事件冒泡
}
},
// ...
};
3.性能優(yōu)化
React 合成事件使用 事件池 來提高性能,即:重(chong)用事件對象。
這意味著:事件回調(diào)函數(shù)執(zhí)行后,事件對象的所有屬性都會被清除并回收以供重用,減少垃圾收集和內(nèi)存使用的壓力。
// 事件對象重用和回收的核心邏輯
SyntheticEvent.prototype.destructor = function() {
this.target = null;
this.currentTarget = null;
this.dispatchConfig = null;
this._targetInst = null;
this.nativeEvent = null;
this.isDefaultPrevented = false;
this.isPropagationStopped = false;
// 清除所有屬性....
};
// Event pool
var eventPool = [];
// 從池中檢索事件對象的函數(shù)
SyntheticEvent.getPooled = function(dispatchConfig, targetInst, nativeEvent, nativeEventTarget) {
if (eventPool.length) {
var instance = eventPool.pop();
SyntheticEvent.call(instance, dispatchConfig, targetInst, nativeEvent, nativeEventTarget);
return instance;
}
return new SyntheticEvent(dispatchConfig, targetInst, nativeEvent, nativeEventTarget);
};
// 將事件對象返回到池的函數(shù)
SyntheticEvent.release = function(event) {
event.destructor();
eventPool.push(event);
};
而 JavaScript 事件每次觸發(fā)時都會創(chuàng)建一個新的事件對象,這可能會增加內(nèi)存使用量和垃圾收集的頻率,特別是當事件頻繁觸發(fā)時(例如:滾動 或 輸入)?!?/p>
4.使用模式
React 通過 JSX語法 允許我們直接在組件的元素上聲明事件處理函數(shù)(合成事件)。
React 會自動處理添加和刪除事件監(jiān)聽器,確保組件安裝時添加監(jiān)聽器,卸載時刪除監(jiān)聽器,從而避免內(nèi)存泄漏。
export default function Button() {
function handleClick() {
console.log('click!');
}
return <button onClick={handleClick}>點我</button>;
}
Javascript 事件需要手動調(diào)用 addEventListener 添加事件監(jiān)聽器,同樣手動調(diào)用removeEventListener 刪除事件監(jiān)聽器:
document.addEventListener('DOMContentLoaded', function() {
const button = document.querySelector('button');
button.addEventListener('click', function() {
console.log('click');
});
});
5.總結(jié)
所以,React 的合成事件是 React 提供了一種高度抽象和集成的事件處理方式,并且可以更好地處理瀏覽器兼容性以及優(yōu)化(卸載事件)等問題。它最終依然會被解析成原生事件。
而 JavaScript 原生事件就是瀏覽器所提供的標準 API,使用時需要更多的處理兼容性以及性能等邊緣邏輯。