React事件機制-事件註冊

来源:https://www.cnblogs.com/raion/archive/2019/03/22/10581960.html
-Advertisement-
Play Games

事件機制 React事件主要分為兩部分: 事件註冊與事件分發。下麵先從事件註冊說起。 事件註冊 假設我們的程式如下: 事件註冊主要發生在初始化Dom屬性的時候,調用 方法,對一些類型dom進行事件綁定。 js switch (tag) { case 'iframe': case 'object': ...


事件機制

本系列以React v16.8.3為基礎進行源碼分析

React事件主要分為兩部分: 事件註冊與事件分發。下麵先從事件註冊說起。

事件註冊

假設我們的程式如下:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>React App</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>
import React from 'react';
import ReactDOM from 'react-dom';

class ClickCounter extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }
  handleClick = () => {
    this.setState((state) => {
      return {count: state.count + 1};
    });
  };
  render() {
    return [
      <button key="1" onClick={this.handleClick}>Update counter</button>,
      <span key="2">{this.state.count}</span>,
    ]
  }
}
ReactDOM.hydrate(<ClickCounter />, document.getElementById('root'));

事件註冊主要發生在初始化Dom屬性的時候,調用setInitialProperties方法,對一些類型dom進行事件綁定。

switch (tag) {
    case 'iframe':
    case 'object':
      trapBubbledEvent(TOP_LOAD, domElement);
      props = rawProps;
      break;

    case 'video':
    case 'audio':
      for (var i = 0; i < mediaEventTypes.length; i++) {
        trapBubbledEvent(mediaEventTypes[i], domElement);
      }

      props = rawProps;
      break;
    ...  
}

setInitialDOMProperties(tag, domElement, rootContainerElement, props, isCustomComponentTag);
...

接著調用setInitialDOMProperties來真正初始化Dom屬性。根據當前workInProgresspendingProps對象,給Dom對象設置屬性。其中,有個分支會專門處理事件。

// registrationNameModules是一個map對象,存儲著React支持的事件類型
 else if (registrationNameModules.hasOwnProperty(propKey)) {
  if (nextProp != null) {
    ensureListeningTo(rootContainerElement, propKey);
  }
}

執行ensureListeningTo方法:

// rootContainerElement為React應用的掛載點, registrationName為onClick
function ensureListeningTo(rootContainerElement, registrationName) {
  // 判斷rootContainerElement是document還是fragment
  var isDocumentOrFragment = rootContainerElement.nodeType === DOCUMENT_NODE || rootContainerElement.nodeType === DOCUMENT_FRAGMENT_NODE;
  // 獲取rootContainerElement所在的document。
  var doc = isDocumentOrFragment ? rootContainerElement : rootContainerElement.ownerDocument;
  listenTo(registrationName, doc);
}

開始執行listenTo方法,註冊事件入口。

// 獲取當前已監聽的原生事件類型的map
var isListening = getListeningForDocument(mountAt);
// 獲取對應的原生事件類型,registrationNameDependencies存儲了React事件類型與瀏覽器原生事件類型映射的一個map
var dependencies = registrationNameDependencies[registrationName];
for (var i = 0; i < dependencies.length; i++) {
    var dependency = dependencies[i];
    if (!(isListening.hasOwnProperty(dependency) && isListening[dependency])) {
        switch (dependency) {
          ...// 除了scroll blur focus cancel close方法調trapCapturedEvent方法,invalid submit reset不處理之外,其餘都調trapBubbledEvent方法。
          default:
          var isMediaEvent = mediaEventTypes.indexOf(dependency) !== -1;
          if (!isMediaEvent) {
            trapBubbledEvent(dependency, mountAt);
          }
          break;
        }
        // 標記該原生事件類型已被註冊,下次註冊同類型事件時會被忽略
        isListening[dependency] = true;  
    }
}

trapCapturedEventtrapBubbledEvent的區別是前者註冊捕獲階段的事件監聽器,後者註冊冒泡階段的事件監聽器。trapCapturedEvent使用比較少,所以重點看下trapBubbledEvent

//click document
function trapBubbledEvent(topLevelType, element) {
  if (!element) {
    return null;
  }
  // 從字面意能看出,前者是交互類事件,優先順序會比普通事件高(click的分發者是dispatchInteractiveEvent)
  var dispatch = isInteractiveTopLevelEventType(topLevelType) ? dispatchInteractiveEvent : dispatchEvent;
    
  // 註冊事件,在冒泡階段捕獲   
  addEventBubbleListener(element, getRawEventName(topLevelType),
  // Check if interactive and wrap in interactiveUpdates
  dispatch.bind(null, topLevelType));
}

總結

可以發現,React把某一類型事件通過事件代理綁定到documentfragment上(fragment的情況比較少)。即workInProgresscomplete過程中,如果之前已經註冊過onClick事件,後續workInProgress中的onClick事件將不再註冊,統一由document中註冊的click事件代理處理。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一.Mysql安裝方式 1.安裝方式 1.rpm,yum安裝 安裝方便,安裝速度快,但無法定製 2.二進位安裝 不需要安裝,解壓即用,不能定製功能 3.編譯安裝 可定製,安裝很慢,安裝分為四個步驟 1.1 解壓(tar) 1.2 生成(./configure)或者cmake 1.3 編譯(make) ...
  • 應用程式慢如牛,原因多多,可能是網路的原因、可能是系統架構的原因,還有可能是資料庫的原因。 那麼如何提高資料庫SQL語句執行速度呢?有人會說性能調優是資料庫管理員(DBA)的事,然而性能調優跟程式員們也有莫大的關係 程式中嵌入的一行行的SQL語句,如果使用了一些優化小技巧,定能達到事半功倍的效果... ...
  • Mysql常用命令 啟動 net start mysql 關閉 net stop mysql 連接mysql mysql uroot ppssword mysql uroot P3307 ppssword 修改密碼 mysqladmin uroot p123456 password 123 增加用戶 ...
  • In order to build a complete ffmpeg with hardware acceleration for Intel platform (XXX lake + Atom), we need a complete Android x86 build, the cross-c ...
  • 程式6:用*號輸出字母C的圖案 console.log(' ****'); console.log(' ***'); console.log(' **'); console.log(' *'); console.log('*'); console.log(' *'); console.log(' * ...
  • 非同步請求 其實什麼是非同步請求已經不用多說了,通俗的說,就是整個頁面不會刷新,需要更新的部分數據做局部刷新,其他數據不變。 學到這裡,你應該用過jquery里的ajax了,所以很能理解了,不多說了。詳細的就自己百度了 在vue中,可以做非同步請求的有vue-resource和axios ... ...
  • global對象 瀏覽器端JavaScript中的全局對象為“window”,在瀏覽器中定義的變數都會成為“window”對象的屬性。 不像瀏覽器端JavaScript,在Nodejs中沒有window對象,Nodejs中的全局對象為“global”,並且我們定義的變數不會作為“global”的屬性 ...
  • 在開始講 Angular 各個核心知識點之前,想先來講講開發工具 WebStorm 的一些配置以及相應配置文件如 tslint.json 的配置。 因為我個人比較註重代碼規範、代碼風格,而對於這些規範,我只有一個觀點: 一切需要依賴開發人員的主觀意識去遵守的規範都沒有多大意義。 以前做 Androi ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...