關於事件,相信都不陌生。 咱們再DOM上綁定事件,並給定一個處理函數。在DOM上事件觸發的時候,會執行處理函數。node
固然也有另一種事件綁定機制---代理。react
通常咱們再用ul元素的時候,咱們的事件通常是綁定在li的父元素ul上,這樣讓ul來代理li的事件,這樣只用綁定一次便可,而React的事件也是使用的代理模式,可是,React的全部事件都是綁定在document上的。瀏覽器
/*
@param id: 事件綁定的當前元素的id
@param registrationName: 綁定的事件名稱,onClick。
@param listener: onClick後的那個回調函數
@param transaction: 一個事務
*/
function enqueuePutListener(id, registrationName, listener, transaction) {
// id是當前元素的id,就是你綁定的事件,好比onClick綁定在哪一個元素上,那個元素的data-reactid屬性的值。
var container = ReactMount.findReactContainerForID(id);
// container就是包裹全部React組件的那個元素。單頁面應用通常只有一個 就是咱們在HTML中定義的那個 <div id="app"></div>
if (container) {
var doc = container.nodeType === ELEMENT_NODE_TYPE ? container.ownerDocument : container;
// doc是一個document
listenTo(registrationName, doc);
// 調用listenTo方法註冊事件。注意,這裏只是將事件註冊在了document上,而沒有給咱們定義的回到函數。
// listenTo方法是ReactBrowserEventEmitter模塊的listenTo方法。
}
// 使用事務,將listenr存儲起來。
transaction.getReactMountReady().enqueue(putListener, {
id: id,
registrationName: registrationName,
listener: listener
});
}
複製代碼
1 listenTo方法: ReactBrowserEventEmitter.listenTo主要的做用即是註冊事件到document上。bash
2 enqueue方法:主要的做用即是將回調函數存儲起來。app
listenTo: function (registrationName, contentDocumentHandle) {
<!--listenTo方法,考慮了太多的可能性,咱們刪除一些無用的代碼,保留核心代碼-->
<!--mountAt就是事件註冊的document(頁面中能夠有多個document)-->
var mountAt = contentDocumentHandle;
<!--// isListening就是當前Document---mountAt,對應的一個惟一的對象。-->
var isListening = getListeningForDocument(mountAt);
<!--定義咱們寫入的事件與top事件的對應。 onAbort:['topAbort'],dependencies對應的是['topAbort']-->
<!--這裏的top事件就是EventConstants模塊中定義的,這些top事件是咱們原始的事件類型,在頂層的一種表示方法。只是換了個名字而已。就像是click事件,咱們綁定的時候是寫做onClick的,可是,React要使用頂層來代理這些事件,因此就寫做了topClick。-->
var dependencies = EventPluginRegistry.registrationNameDependencies[registrationName];
<!--EventPluginRegistry模塊的registrationNameDependencies屬性是一個對象,主要是保存了咱們寫做的事件與頂層事件的一一對應關係。就像是
{
onAbort:['topAbort'],onClick:['topClick']
}
-->
ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(dependency, topEventMapping[dependency], mountAt);
<!--這裏的核心調用了trapBubbledEvent方法,也有一些狀況是使用 trapCapturedEvent方法顧名思義,他們是在將事件註冊在不一樣的事件流中(捕獲事件流或者是冒泡事件流),下面咱們以冒泡事件流爲例說明。-->
}
複製代碼
// @param topLevelType:topAbort,topClick等
// @param handlerBaseName:abort,click等
// @param handle:一個document
trapBubbledEvent: function (topLevelType, handlerBaseName, handle) {
var element = handle;
if (!element) {
return null;
}
/*
注意:
listen方法主要是調用了addEventListener方法。而addEventListener方法接受三個參數。
// @param eventType:就是click,abort等。
// @param callback :就是事件觸發的時候的回調函數。
// @param Boolean值: 表示在何時觸發,冒泡階段,仍是捕獲階段默認是false,冒泡階段。
而到了這裏,咱們沒有拿到一個回調函數callback,你看給的三個參數是:topLevelType, handlerBaseName, handle
這裏React給了一個函數 ReactEventListener.dispatchEvent.bind(null, topLevelType)來作回調函數。
這個回調函數,和咱們綁定在頁面上的回調函數不是一回事。
咱們在頁面上綁定的回調函數,最終是存儲在了EventPluginHub的listenerBank中了。
*/
<!--核心仍是調用了EventListener模塊的listen方法-->
return EventListener.listen(element, handlerBaseName, ReactEventListener.dispatchEvent.bind(null, topLevelType));
},
複製代碼
<!-- 該方法就是調用了註冊方法的API。抹平了不一樣瀏覽器的差別。在Target上註冊了一個事件類型爲eventType的事件
target是上邊傳過來的element,也就是一個document。
eventType:是一個事件類型,好比click,change等。
callback:咱們以前說過,咱們再組件內寫的方法是沒有註冊在這的。
這裏的callback是React提供的一個分發函數。ReactEventListener.dispatchEvent()。而咱們在組件中寫的方法最終是存儲到了EventPluginHub中。
-->
listen: function (target, eventType, callback) {
if (target.addEventListener) {
target.addEventListener(eventType, callback, false);
return {
remove: function () {
target.removeEventListener(eventType, callback, false);
}
};
} else if (target.attachEvent) {
target.attachEvent('on' + eventType, callback);
return {
remove: function () {
target.detachEvent('on' + eventType, callback);
}
};
}
},
複製代碼