React Hooks 的實現必須依賴 Fiber 麽?

来源:https://www.cnblogs.com/cloudwise/archive/2022/04/20/16170959.html
-Advertisement-
Play Games

作者:zxg_神說要有光 原文鏈接:https://juejin.cn/post/7087172219226292237 React 的 hooks 是在 fiber 之後出現的特性,所以很多人誤以為 hooks 是必須依賴 fiber 才能實現的,其實並不是,它們倆沒啥必然聯繫。 現在,不止 re ...


作者:zxg_神說要有光

原文鏈接:https://juejin.cn/post/7087172219226292237

React 的 hooks 是在 fiber 之後出現的特性,所以很多人誤以為 hooks 是必須依賴 fiber 才能實現的,其實並不是,它們倆沒啥必然聯繫。

現在,不止 react 中實現了 hooks,在 preact、react ssr、midway 等框架中也實現了這個特性,它們的實現就是不依賴 fiber 的。

我們分別來看一下這些不同框架中的 hooks 都是怎麼實現的:

react 如何實現 hooks

react 是通過 jsx 描述界面的,它會被 babel 或 tsc 等編譯工具編譯成 render function,然後執行產生 vdom:

這裡的 render function 在 React17 之前是 React.createElement:

在 React 17 之後換成了 jsx:

這個 jsx-runtime 會自動引入,不用像之前那樣每個組件都要保留一個 React 的 import 才行。

render function 執行產生 vdom:

vdom 的結構是這樣的:

在 React16 之前,會遞歸渲染這個 vdom,增刪改真實 dom。

而在 React16 引入了 fiber 架構之後就多了一步:首先把 vdom 轉成 fiber,之後再渲染 fiber。

vdom 轉 fiber 的過程叫做 reconcile,最後增刪改真實 dom 的過程叫做 commit。

為什麼要做這樣的轉換呢?

因為 vdom 只有子節點 children 的引用,沒有父節點 parent 和其他兄弟節點 sibling 的引用,這導致了要一次性遞歸把所有 vdom 節點渲染到 dom 才行,不可打斷。

萬一打斷了會怎麼樣呢?因為沒有記錄父節點和兄弟節點,那隻能繼續處理子節點,卻不能處理 vdom 的其他部分了。

所以 React 才引入了這種 fiber 的結構,也就是有父節點 return、子節點 child、兄弟節點 sibling 等引用,可以打斷,因為斷了再恢復也能找到後面所有沒處理過的節點。

fiber 節點的結構是這樣的:

這個過程可以打斷,自然也就可以調度,也就是 schdule 的過程。

所以 fiber 架構就分為了 schdule、reconcile(vdom 轉 fiber)、commit(更新到 dom)三個階段。

函數組件內可以用 hooks 來存取一些值,這些值就是存在 fiber 節點上的。

比如這個函數組件內用到了 6 個 hook:

那麼對應的 fiber 節點上就有個 6 個元素的 memorizedState 鏈表:

通過 next 串聯起來:

不同的 hook 在 memorizedState 鏈表不同的元素上存取值,這就是 react hooks 的原理。

這個鏈表有創建階段和更新階段,所以你會發現 useXxx 的最終實現都分為了 mountXxx 和 updateXxx:

這裡的 mount 階段就是創建 hook 節點並組裝成鏈表的:

會把創建好的 hook 鏈表掛到 fiber 節點的 memorizedState 屬性上。

那更新的時候自然也就能從 fiber 節點上取出這個 hook 鏈表:

這樣在多次渲染中,useXxx 的 api 都能在 fiber 節點上找到對應的 memorizedState。

這就是 react hooks 的原理,可以看到它是把 hook 存在 fiber 節點上的。

那 preact 有什麼不同呢?

preact 如何實現 hooks

preact 是相容 react 代碼的更輕量級的框架,它支持 class 組件和 function 組件,也支持了 hooks 等 react 特性。不過它沒有實現 fiber 架構。

因為它主要考慮的是體積的極致(只有 3kb),而不是性能的極致。

剛纔我們瞭解了 react 是把 hook 鏈表存放在 fiber 節點上的,那 preact 沒有 fiber 節點,會把 hook 鏈表存在哪呢?

其實也很容易想到,fiber 只是對 vdom 做了下改造用於提升性能的,和 vdom 沒啥本質的區別,那就把 hook 存在 vdom 上不就行了?

確實,preact 就是把 hook 鏈表放在了 vdom 上。

比如這個有 4 個 hooks 的函數組件:

它的實現就是在 vdom 上存取對應的 hook:

它沒有像 react 那樣把 hook 分為 mount 和 update 兩個階段,而是合併到一起處理了。

如圖,它把 hooks 存在了 component.__hooks 的數組上,通過下標訪問。

這個 component 就是 vdom 上的一個屬性:

也就是把 hooks 的值存在了 vnode._component._hooks 的數組上。

對比下 react 和 preact 實現 hooks 的差異:

  • react 中是把 hook 鏈表存放在 fiberNode.memorizedState 屬性上,preact 中是把 hook 鏈表存放在 vnode._component._hooks 屬性上
  • react 中的 hook 鏈表通過 next 串聯,preact 中的 hook 鏈表就是個數組,通過下標訪問
  • react 把 hook 鏈表的創建和更新分離開,也就是 useXxx 會分為 mountXxx 和 updateXxx 來實現,而 preact 中合併在一起處理的

所以說,hooks 的實現並不依賴 fiber,它只不過是找個地方存放組件對應的 hook 的數據,渲染時能取到就行,存放在哪裡是無所謂的。

因為 vdom、fiber 和組件渲染強相關,所以存放在了這些結構上。

像 react ssr 實現 hooks,就既沒有存在 fiber 上,也沒有存在 vdom 上:

react ssr 如何實現 hooks

其實 react-dom 包除了可以做 csr 外,也可以做 ssr:

csr 時使用 react-dom 的 render 方法:

ssr 的時候使用 react-dom/server 的 renderToString 方法或 renderToStream 方法:

大家覺得 ssr 的時候會做 vdom 到 fiber 的轉換麽?

肯定不會呀,fiber 是為了提高在瀏覽器中運行時的渲染性能,把計算變成可打斷的,在空閑時做計算,才引入的一種結構。

服務端渲染自然就不需要 fiber。

不需要 fiber 的話,它把 hook 鏈表存放在哪裡呢?vdom 麽?

確實可以放在 vdom,但是其實並沒有。

比如 useRef 這個 hooks:

它是從 firstWorkInProgressHook 開始的用 next 串聯的一個鏈表。

而 firstWorkInProgressHook 最開始用 createHook 創建的第一個 hook 節點:

並沒有掛載到 vdom 上。

為什麼呢?

因為 ssr 只需要渲染一次呀,又不需要更新,自然沒必要掛到 vdom 上。

只要每次處理完每個組件的 hooks 就清空一下這個 hook 鏈表就行:

所以,react ssr 時,hooks 是存在全局變數上的。

對比下 react csr 和 ssr 時的 hooks 實現原理的區別:

  • csr 時會從 vdom 創建 fiber,用於把渲染變成可打斷的,通過空閑調度來提高性能,而 ssr 時不會,是 vdom 直接渲染的
  • csr 時把 hooks 保存到了 fiber 節點上,ssr 時是直接放在了全局變數上,每個組件處理完就清空。因為不會用第二次了
  • csr 時會把 hook 的創建和更新分為 mount 和 update 兩個階段,而 ssr 因為只會處理一次,只有創建階段

hooks 的實現原理其實不複雜,就是在某個上下文中存放一個鏈表,然後 hooks api 從鏈表不同的元素上訪問對應的數據來完成各自的邏輯。這個上下文可以是 vdom、fiber 甚至是全局變數。

不過 hooks 這個思想還是挺火的,淘寶出的服務端框架 midway 就在引入了 hooks 的思想:

midway 如何實現 hooks

midway 是一個 Node.js 框架:

服務端框架自然就沒有 vdom、fiber 這種結構,不過 hooks 的思想並不依賴這些,實現 hooks 的 api 只需要在某個上下文放一個鏈表就行。

midway 就實現了類似 react hooks 的 api:

具體它這個 hook 鏈表存在哪我還沒看,不過我們已經掌握 hooks 的實現原理了,只要有個上下文存放 hook 鏈表就行,在哪都可以。

總結

react hooks 是在 react fiber 架構之後出現的特性,很多人誤以為 hooks 必須配合 fiber 才能實現,我們分別看了 react、preact、react ssr、midway 中的 hooks 的實現,發現並不是這樣的:

  • react 是把 vdom 轉成 fiber,然後把 hook 鏈表存放到了 fiber.memorizedState 屬性上,通過 next 串聯
  • preact 沒有實現 fiber,它是把 hook 鏈表放到了 vnode._component._hooks 屬性上,數組實現的,通過下標訪問
  • react ssr 時不需要 fiber,但是也沒有把 hook 鏈表掛到 vdom 上,而是直接放在了一個全局變數上,因為只需要渲染一次,渲染完一個組件就清空這個全局變數就行
  • midway 是一個 Node.js 框架,它也實現了 hooks 類似的 api,具體放在哪我們沒深入,但是只要有個上下文存放 hook 鏈表就行

所以,react hooks 必須依賴 fiber 才能實現麽?

明顯不是,搭配 fiber、搭配 vdom、搭配全局變數,甚至任何一個上下文都可以。

寫在最後

近年來,在AIOps領域快速發展的背景下,IT工具、平臺能力、解決方案、AI場景及可用數據集的迫切需求在各行業迸發。基於此,雲智慧在2021年8月發佈了AIOps社區, 旨在樹起一面開源旗幟,為各行業客戶、用戶、研究者和開發者們構建活躍的用戶及開發者社區,共同貢獻及解決行業難題、促進該領域技術發展。

社區先後 開源 了數據可視化編排平臺-FlyFish、運維管理平臺 OMP 、雲服務管理平臺-摩爾平臺、 Hours 演算法等產品。

可視化編排平臺-FlyFish:

項目介紹:https://www.cloudwise.ai/flyFish.html

Github地址: https://github.com/CloudWise-OpenSource/FlyFish

Gitee地址: https://gitee.com/CloudWise/fly-fish

行業案例:https://www.bilibili.com/video/BV1z44y1n77Y/

部分大屏案例:


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

-Advertisement-
Play Games
更多相關文章
  • Linux 配置免密登錄 慄子 生成公鑰和私鑰 ssh-keygen -t rsa Tips: 執行後會在~/.ssh/目錄下創建 id_rsa 和 id_rsa.pub 文件 生成省份認證文件 ssh-copy-id -i ~/.ssh/id_rsa.pub [email protected] T ...
  • 工欲善其事,必先利其器,對於每次都要人工統計的發版數據,嗅到了一絲絲優化的空間……難倒我的不是 curl 使用 cookie 訪問 web 伺服器,而是如何解析 json 中數組的數組… ...
  • 本文介紹如何使用 SQL ORDER BY 子句,對檢索出的數據進行排序。根據需要,可以利用它在一個或多個列上對數據進行排序。 一、排序數據 正如 如何使用 SQL 的 SELECT 語句從表中檢索一個或多個數據列 所述,下麵的 SQL 語句返回某個資料庫表的單個列。但請看其輸出,並沒有特定的順序。 ...
  • 在本文中,我們將介紹 GraphScope 圖互動式查詢引擎 GAIA-IR,它支持高效的 Gremlin 語言表達的交互圖查詢,同時高度抽象了圖上的查詢計算,具有高可擴展性。 ...
  • 在我還在手動調整兩個控制項距離時,第一次見識到了marginStart marginEnd兩個用法, 但是,只知道它倆能夠調整控制項距離,但是不知道具體用法,老師也沒細說,就這樣開啟了我對於這兩個用法“探索”之路 ①首先,我對於兩個控制項之間的調整,是需要將其兩個控制項點連接起來,才能夠被使用。 ②其次,對 ...
  • Xamarin作為微軟提供的移動服務多系統開發平臺,成為很多開發者首選的應用開發平臺。AppGallery Connect(以下簡稱AGC)也在逐步的支持Xamarin的SDK。今天就手把手教大家如何快速接入Xamarin版遠程配置服務。 接入步驟 安裝Xamarin環境 Xamarin的御用開發平 ...
  • HMS Core廣告服務開髮指南中提到“xxxx為測試專用的廣告位ID,App正式發佈時需要改為正式的廣告位ID”,那麼今天咱們就來說說,怎麼獲取正式的廣告位ID。 測試廣告位ID和正式廣告位ID有什麼區別? 測試廣告位ID:不區分應用和條件,在中國大陸和非中國大陸都可以請求廣告,有利於開發者在集成 ...
  • 這幾天電腦有點問題,一直在弄,而且論文也逼近了也在時間弄那個 ,前面node有一個大項目,已經做完了,我現在是準備把上次複習斷下的繼續複習一直到這個項目,然後就開始vue了。 1. 首先是函數的一個進階,要明白函數也是對象,所以是可以通過new的方法來創建實例的。 然後是調用函數的三個方法:call ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...