react17源碼中部分二進位計算的解釋

来源:https://www.cnblogs.com/ZiLongZiLong/archive/2023/10/04/17144879.html
-Advertisement-
Play Games

安裝導入 npm npm i three 導入 並非所有功能都在three,還需從子目錄導入 // three模塊 import * as three from 'three' // 一些不在three模塊的功能,這裡是OrbitControls導入示例 import { OrbitControls ...


theme: qklhk-chocolate
highlight: a11y-dark

react17放棄了之前的expirationTime而啟用了lane模型,故而在原來16的基礎上又產生了更多的二進位運算,在接下來的一段時間我打算把這些二進位運算都整明白了、

關於react為什麼會啟用lane模型的官方解釋
js中的二進位位運算都是以32位補碼的形式計算的,更多解釋可以參考mdn

1.關於上下文的切換

在react的更新中,executionContext按照字面意思即為執行上下文,executionContext的預設值是0NoContext, 此後的executionContext的更新中,都是與其他不同的上下文以按位或的運算的方式進行更新的,react17里的不同上下文有如下8種:

var NoContext =
/*             */
0;
var BatchedContext =
/*               */
1;
var EventContext =
/*                 */
2;
var DiscreteEventContext =
/*         */
4;
var LegacyUnbatchedContext =
/*       */
8;
var RenderContext =
/*                */
16;
var CommitContext =
/*                */
32;
var RetryAfterError =
/*       */
64;

分析位運算可能寫出他們的二進位形式更加清晰一點,由上到下依次為:

NoContext              0000000
BatchedContext         0000001
EventContext           0000010
DiscreteEventContext   0000100
LegacyUnbatchedContext 0001000
RenderContext          0010000
CommitContext          0100000
RetryAfterError        1000000

實際參與計算時應該是32位的但是這裡取7位是因為2^6是64多餘的0對於我的分析來說是沒有意義的。
react17一共在11個地方對context進行了|=方式的更新,不同的更新方式對應了不同的context的變數,比方,batchedUpdates的更新為executionContext |= BatchedContext;,unbatchedUpdates中的更新則相應的為executionContext |= LegacyUnbatchedContext

這些更新在其他地方大同小異,基本都是|=方式進行更新, 但在unbatchedUpdates更新前,卻進行了一次這個操作:executionContext &= ~BatchedContext,這裡我們先不管這段代碼具體是什麼作用,我們暫且討論一下這個二進位運算會產生何種效果。首先假設在某一狀態時 executionContext 與 BatchedContext 發生了一次運算:

 executionContext | BatchedContext
 0000000          | 0000001
 executionContext	= 	0000001	   

那麼 executionContext &= ~BatchedContext:

	executionContext & ~BatchedContext
	0000001          &  1111110
	executionContext =  0000000

可以看到executionContext &= ~BatchedContext的效果其實就是還原了上次executionContext |= BatchedContext;前的executionContext。這個其實很好理解,要知道所有的context他在以上的7位二進位中只占了其中的一位,那麼無論之前的executionContext與其中那個context進行按位或|運算,其結果就是只是讓executionContext的某個位為1,拿BatchedContext舉例他只是使得executionContext右邊的第一位為1,而在按位取反之後,除了自己所占的那個位為0其餘都成了1,再與executionContext進行按位與&運算,則executionContext中其他位為1的(也就是說executionContext可能與其他上下文也一起進行了|)是不會變得,只有~BatchedContext對應的那一位現在是0,因此不管executionContext中這個位置的數字是0還是1其結果最後都為0,也就是上邊說的executionContext還原到了與BatchedContext|=前的狀態。這條準則換成其他任何一個context都是一樣的。

2.關於上下文的判斷

拿一個地方舉例:

if (!((executionContext & (RenderContext | CommitContext)) === NoContext)) {
    {
      throw Error( "Should not already be working." );
    }
  } 

這個if里的表達式為真,即 左邊的要不等於NoContext,左邊的不等於NoContext則說明executionContext里屬於RenderContext或CommitContext的那個位為1,而那個位為1就說明 executionContext肯定與其中之一發生了按位或運算,而發生按位或也就代表著某個地方的代碼執行過了。以上。

小結: 其實以上這種二進位的運算,應該屬於二進位掩碼的應用,二進位的運算除了數學上的特征比方左移右移相當於乘以2除以2,其他方面更覺得像一種圖形一樣的運算,因為 & | 這兩種運算是不會產生進位的,因此看源碼的二進位運算更多的應該從類似圖形變換的角度去理解每個二進位運算的含義,而不是數字之間的運算。

3.關於lane

關於lane有篇文章個人決得講的特別好,推薦一看

關於lane的基本解釋

react17中使用了31位二進位來表示lane的概念,其中31位中占一位的變數稱作lane,占據多位的稱為lanes,react17中全部lane如下(二進位形式):

export const NoLanes: Lanes = /*                        */ 0b0000000000000000000000000000000;
export const NoLane: Lane = /*                          */ 0b0000000000000000000000000000000;

export const SyncLane: Lane = /*                        */ 0b0000000000000000000000000000001;
export const SyncBatchedLane: Lane = /*                 */ 0b0000000000000000000000000000010;

export const InputDiscreteHydrationLane: Lane = /*      */ 0b0000000000000000000000000000100;
const InputDiscreteLanes: Lanes = /*                    */ 0b0000000000000000000000000011000;

const InputContinuousHydrationLane: Lane = /*           */ 0b0000000000000000000000000100000;
const InputContinuousLanes: Lanes = /*                  */ 0b0000000000000000000000011000000;

export const DefaultHydrationLane: Lane = /*            */ 0b0000000000000000000000100000000;
export const DefaultLanes: Lanes = /*                   */ 0b0000000000000000000111000000000;

const TransitionHydrationLane: Lane = /*                */ 0b0000000000000000001000000000000;
const TransitionLanes: Lanes = /*                       */ 0b0000000001111111110000000000000;

const RetryLanes: Lanes = /*                            */ 0b0000011110000000000000000000000;

export const SomeRetryLane: Lanes = /*                  */ 0b0000010000000000000000000000000;

export const SelectiveHydrationLane: Lane = /*          */ 0b0000100000000000000000000000000;

const NonIdleLanes = /*                                 */ 0b0000111111111111111111111111111;

export const IdleHydrationLane: Lane = /*               */ 0b0001000000000000000000000000000;
const IdleLanes: Lanes = /*                             */ 0b0110000000000000000000000000000;

export const OffscreenLane: Lane = /*                   */ 0b1000000000000000000000000000000;
關於lane的基本使用
1. 創建fiber

每一個fiber創建的時候其lanes,childLanes欄位都被初始化為NoLanes

2. 創建update

react中無論是初始的渲染,還是setstate或者由hooks派發出來的更新操作,都會調用createupdate方法創建一個update對象,不同之處是,對於更新時的update對象來說lane欄位是什麼,是由與之相關的fiber的mode欄位決定的:

...
var lane = requestUpdateLane(fiber);
var update = createUpdate(eventTime, lane);
...
function requestUpdateLane(fiber) {
  ...
  
  var mode = fiber.mode;

  if ((mode & BlockingMode) === NoMode) {
    return SyncLane;
  } else if ((mode & ConcurrentMode) === NoMode) {
    return getCurrentPriorityLevel() === ImmediatePriority$1 ? SyncLane : SyncBatchedLane;
  } 
  ...
}

mode一般來說有如下幾種:

var NoMode = 0;
var StrictMode = 1;

var BlockingMode = 2;
var ConcurrentMode = 4;
var ProfileMode = 8;
var DebugTracingMode = 16;

HostRootFiber(整個react應用的初始fiber節點)初始化的時候,目前來看其tagLegacyRoot,在createHostRootFiber方法中賦予其mode:

if (tag === ConcurrentRoot) {
  mode = ConcurrentMode | BlockingMode | StrictMode;
} else if (tag === BlockingRoot) {
  mode = BlockingMode | StrictMode;
} else {
  mode = NoMode;
}

由其tag可以知道HostRootFiber的mode即為noMode,之後在beginwork開始創建各個子節點的fiber時,其fiber的mode直接繼承自父節點:

      var _created4 = createFiberFromElement(element, returnFiber.mode, lanes);

因此 對於大部分fiber來說,在一次更新中由其派發的update的lane是SyncLane。

3.更新過程中對fiber上各個欄位的更新

每個更新時都會自scheduleUpdateOnFiber始,而在scheduleUpdateOnFiber中,會

  1. 更新fiber上的lanes欄位:
	sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane);

然後沿fiber樹向上遍歷,更新每個父節點fiber的childLanes欄位

while (parent !== null) {
    ...
    parent.childLanes = mergeLanes(parent.childLanes, lane);
    ...
}

其中mergeLanes 就是將兩個變數進行按位或運算,產生新的lanes。 即由此可以看到,當前各種各樣的更新的lane最終都會在根節點的childLanes欄位上有體現。

  1. 更新root根節點的各個欄位
  • pendingLanes:
root.pendingLanes |= updateLane;
  • suspendedLanes,pingedLanes
var higherPriorityLanes = updateLane - 1; // Turns 0b1000 into 0b0111
root.suspendedLanes &= higherPriorityLanes;
root.pingedLanes &= higherPriorityLanes;

幾句解釋: higherPriorityLanes - 1 ,比方代碼中的註釋Turns 0b1000 into 0b0111,他假如是一個lanes欄位,那麼他的值就是比當前updateLane的優先順序更高的各個lane按位或之後的結果,因為結合前邊各個lane的值可以看到,越靠近右邊的1的位置的優先順序越高, 至於suspendedLanes,pingedLanes的更新就是保留了比當前優先順序更高的lane。

  • eventTimes
    這是一個31長度的數組,每一位對應一個lane
var eventTimes = root.eventTimes;
var index = laneToIndex(updateLane); // 獲取當前lane在eventTimes的數組索引
// We can always overwrite an existing timestamp because we prefer the most
/ recent event, and we assume time is monotonically increasing.
eventTimes[index] = eventTime;//eventTime是創建當前update的時間

未完待續。。。


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

-Advertisement-
Play Games
更多相關文章
  • Spring是分層的JavaEE應用一站式的輕量級開源框架,以控制反轉(Inverse orControl,loC)和麵向切麵編程(Aspect Oriented Programming,AOP)為內核,提供了表現層Spring MVC、持久層Spring JDBC以及業務層事務管理的眾多的企業級應... ...
  • 在之前的文章《網路編程雜談之TCP協議》中,我們闡述了TCP協議的基本概念,TCP作為一種可靠的、面向連接的數據傳輸協議,確保了數據在發送和接收之間的可靠性、順序性和完整性,特點可以概括如下: 1、面向連接:在進行數據傳輸之前,TCP需要客戶端和伺服器之間建立一個連接,這個連接包括一系列的握手和協商 ...
  • 為什麼要使用skb_reserve函數把邊界對齊 skb_reserve 函數通常用於網路編程中的數據包處理,特別是在構建自定義協議棧或數據包處理模塊時。它的作用是為數據包的頭部預留額外的空間,以確保數據包的頭部數據在記憶體中是對齊的。 邊界對齊的概念是因為許多硬體平臺和網路協議要求數據包頭的位元組對齊 ...
  • Linux系統 一切皆文件 (模板機:1. 配置IP地址;2. 關閉防火牆、selinux安全載入機制) 一、配置IP地址 cd /etc/sysconfig/network-scripts/ ls ifcfg-eth0文件解析(網卡配置文件): if:interface 介面 cfg:config ...
  • 此LIN UDS bootloader的上位機是zFlash, LIN盒子是自己開發的,更新應用程式時bootloader和上位機zFlash間通訊採用UDS協議 ...
  • 簡單的商城系統的資料庫設計 本文會詳細介紹一下,簡單商城系統的資料庫的表的創建。 本文使用的資料庫是MySQL8.0.x。 資料庫可視化軟體使用的是jetbrains datgrip。 用戶相關 賬號表 create table account( id int8 auto_increment pri ...
  • 1.背景 H5 頁面做秒開優化是業務的常規操作,一般正常通過網路請求的 H5 頁面,我們都是圍繞資源載入速度優化展開。優化手段主要分兩個方向,一個是提升網路速度,一個是減少資源大小。 提升網路速度,一般的手段有 DNS 預解析、多功能變數名稱、升級 HTTP2、使用 CDN、SSR。而即使有靜態資源的網路緩 ...
  • 經過 Adobe 工程師多年來的努力,並與 Chrome 等瀏覽器供應商密切合作,通過 WebAssembly + Emscripten、Web Components + Lit、Service Workers + Workbox 和新的 Web API 的支持,終於在近期推出了 Web 版 Pho ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...