React 並發功能體驗(前端的並發模式)
React 是一個開源 JavaScript 庫,開發人員使用它來創建基於 Web 和移動的應用程序,並且支持構建交互式用戶界面和 UI 組件。React 是由 Facebook 軟件工程師 Jordan Walke 創建,React 的第一個版本在七年前問世,現在,Facebook 負責維護。React框架自首次發佈以來,React 的受歡迎程度直線飆升,熱度不減。
2020 年 10 月,React 17 發佈瞭,但令人驚訝的是——“零新功能”。當然,這並不是真的表示沒有任何新添加的功能,讓廣大程序員使用者興奮。事實上,這個版本為我們帶來瞭很多重大功能的升級及16版本的bug修復,並推出瞭:Concurrent Mode 和Suspense。
雖然這兩個功能尚未正式發佈,這些功能已提供給開發人員進行測試。一旦發佈,它們將改變 React 呈現其 UI 的方式,從而達到雙倍提高性能和用戶體驗。
簡要說明, Concurrent Mode 和Suspense 可以使用戶無縫處理數據加載,加載狀態,用戶界面操作更加平滑和無縫切換。 在Concurrent Mode 下,React可以暫停高消耗的,非緊急的組件的渲染,並聚焦在更加緊迫的任務處理,如UI 渲染,始終保持應用為可響應式,避免白屏,卡頓等現象。
本文主要分享深入瞭解Concurrent Mode 和Suspense 模式下的數據提取功能。
為什麼需要 Concurrent Mode?
眾所周知,JavaScript 框架或庫是單線程的工作。因此,當一個代碼塊運行時,其餘的塊必須等待執行。無法並發執行多線程工作。界面渲染也是一樣的。
一旦 React 開始渲染某些東西,無法中斷直到運行完成。React 開發人員將這種渲染稱為“阻塞渲染”。 這種阻塞渲染會創建一個不穩定的用戶界面,並且隨時可能停止響應。
具體問題
假如,我們需要顯示一個很長的可選列表用於過濾產品的應用程序。我們使用搜索框用於過濾記錄,設計方案是當用戶點擊搜索按鈕後,用戶界面需要重新刷新列出相關聯的數據。
如果列表過長,數據過多,UI“卡頓”,即渲染對用戶可見。這種卡頓也會大大降低產品性能。開發人員可以使用一些技術,如節流和防抖,這些技術會有一定幫助,但不是完美的解決方案。
節流限制特定函數被調用的次數。使用節流,我們可以避免重復調用昂貴和耗時的API或函數。這個過程能夠提高性能,尤其是在用戶界面上呈現信息。
防抖會在預定的時間內忽略對函數的調用。函數調用僅在經過預定時間後進行。
下圖描述瞭卡頓現象:
在等待非緊急 API 調用完成時,UI 卡頓,從而阻止呈現用戶界面。解決方案是使用並發模式進行可中斷渲染。
無中斷渲染
通過可中斷渲染,React.js 在處理和重新渲染列表時不會阻塞 UI。它通過暫停瑣碎的工作、更新 DOM 並確保 UI 不會卡頓,使 React.js 更加細化。React 使用用戶輸入並行更新或重繪輸入框。React 使用用戶輸入並重繪輸入框並行執行。它還更新內存中的列表。React 完成更新後,它會更新 DOM 並在用戶的顯示器上重新呈現列表。本質上,無中斷渲染使 React 能夠“多任務”。此功能提供瞭更流暢的 UI 體驗。
並發模式
並發模式是一組功能,可幫助 React 應用程序保持響應並平滑地適應用戶的設備和網絡速度能力。並發模式將其擁有的任務劃分為更小的塊。 React 的調度程序可以挑選並選擇要執行的作業。作業的調度取決於它們的優先級。通過對任務進行優先級排序,它可以停止瑣碎或不緊急的事情,或者進一步推動它們。 React 始終將用戶界面更新和渲染放在首位。
使用並發模式,我們可以:
- 控制首次渲染過程
- 優先處理渲染過程
- 暫停和恢復組件的渲染
- 緩存和優化組件的運行時渲染
- 隱藏顯示內容直到需要展示時
隨著 UI 渲染,並發模式改進瞭對傳入數據的響應,懶加載控件,異步處理過程。並發模式保證瞭用戶界面始終處於激活狀態,並且持續在後臺更新數據,並發模式也始終使用React 的兩個鉤掛:useTransition
和useDeferredValue
使用useDeferredValue Hook
useDeferredValue Hook
的定義如下:
const deferredValue = useDeferredValue(value, { timeoutMs: <some value> });
此命令設置值在timeoutMs
中設置的時間後“滯後”。 用戶界面是必須立即更新還是必須等待數據,該命令使用戶界面保持激活狀態和響應性,該Hook避免瞭 UI 卡頓,並始終保持用戶界面響應,以保持獲取數據滯後的較小成本。
使用 Transition Hook
useTransition Hook
是React
中主要用於掛起的Hook
,假設這樣的場景下:其中有一個帶有用戶名按鈕的網頁。隻需點擊一個按鈕,網頁就會在屏幕上顯示用戶的詳細信息。
假設用戶首先單擊一個按鈕,然後單擊下一個。屏幕要麼變成空白,要麼我們在屏幕上看到一個微調器。如果獲取詳細信息花費的時間太長,用戶界面可能會凍結。
useTransition
方法返回兩個Hook
的值:startTransition
和 isPending
。定義的語法如下:
const [startTransition, isPending] = useTransition({ timeoutMs: 3000 });
startTransition
定義的語法:
<button disabled={isPending} startTransition(() => { <fetch Calls> }); </button> {isPending? " Loading...": null}
使用 useTransition
鉤子,React.js
繼續顯示沒有用戶詳細信息的用戶界面,直到用戶詳細信息準備好,但 UI 是響應式的。React
優先考慮用戶界面,以在並行獲取數據時保持響應。
為獲取數據的Suspense
Suspense
是React
與並發模式一起引入的另一個實驗性功能。Suspense
使組件能夠在渲染前等待一段預定的時間。
Suspense
的主要作用是從組件異步讀取數據,而無需擔心數據的來源。Suspense
最適合延遲加載的概念。Suspense
允許數據獲取庫通知React
數據組件是否可以使用。在必要的組件準備就緒之前,React
不會更新 UI。
使用Suspense
的好處:
1.數據獲取庫和React
組件之間的集成
2.控制視覺加載狀態
3.避免競爭條件
Spinner
組件的基本語法如下:
import Spinner from './Spinner'; <Suspense fallback={<Spinner />}> <SomeComponent /> </Suspense>
Concurrent Mode
中使用的Suspense
允許耗時的組件在等待數據的同時開始渲染。同時顯示占位符。這種組合產生瞭更流暢的UI體驗。
Suspense 和 懶加載組件
React.lazy
是一個新功能,它使React.js
能夠延遲加載組件。懶加載意味著僅在需要時才加載組件(檢索和呈現它們的代碼)。他們會優先考慮最關鍵的用戶界面組件。React
開發人員建議將懶加載組件包裝在Suspense
組件中。
這樣做可確保組件在渲染時不會出現“不良狀態”。用戶界面在整個過程中保持響應,並帶來更流暢的用戶體驗。
啟用並發模式
要啟用並發模式,請安裝最新的測試版本。安裝 React 的先決條件是節點數據包管理器 (npm)。要安裝測試版本,請執行以下命令:
npm install react@experimental react-dom@experimental
要測試是否設置瞭測試版本,請創建一個示例 React 應用程序。沒有測試功能的渲染代碼如下:
import * as React from 'react'; import { render } from 'react-dom'; render(<App />, document.getElementById('root'));
並發模式的,具體代碼如下:
import * as React from 'react'; import { createRoot } from 'react-dom'; createRoot(document.getElementById('root')).render(<App />);
這將為整個應用程序啟用並發模式。React 將渲染調用分為兩部分:
- 創建根元素
- 使用渲染調用
目前,React 計劃維護三種模式:
- 傳統模式是向後兼容的傳統或當前模式
- 阻塞模式是並發模式開發的中間階段
- 並發模式
阻塞模式是使用createBlockingRoot 調用來替換createRoot 調用,在並發模式的開發情況下,阻塞模式為開發者提供瞭機會來修復bug或解決問題。
React 官方文檔中也說明瞭每種模式支持的功能:
示例應用:
本文也創建瞭一個測試程序來驗證並發模式和其他模式的用法和效果。本文以像素應用為例在150*150的畫佈上隨機分佈像素並包含一個搜索框,每次用戶點擊搜索框時候,畫佈會重新渲染自己。
即使UI 界面無法在並發模式下渲染,用戶輸入也不會停止更新。像素畫佈在處理完成後重新渲染。在傳統模式下,快速鍵入時,UI 會停止,有時會在再次渲染畫佈之前停止。用戶輸入也會停止並且不會更新。
構建像素應用程序的主要文件是 canvas.js。我們還制作瞭一個輸入框,用戶可以在其中輸入任何內容。每次按下一個鍵都會重新渲染像素畫佈。
代碼示例:Index.js
import React from "react"; import ReactDOM from "react-dom"; import App from "./App"; // Traditional or non-Concurrent Mode react const rootTraditional = document.getElementById("root"); ReactDOM.render(<App caption="Traditional or Block Rendering" />, rootTraditional); // Concurrent Mode enabled const rootConcurrent = document.getElementById("root-concurrent"); ReactDOM.createRoot(rootConcurrent).render(<App caption="Interruptible Rendering" />);
App.js
import React, { useState, useDeferredValue } from "react"; import "./App.css"; import { Canvas } from "./Canvas"; export default function App(props) { const [value, setValue] = useState(""); //This is available only in the Concurrent mode. const deferredValue = useDeferredValue(value, { timeoutMs: 5000 }); const keyPressHandler = e => { setValue(e.target.value); }; return ( <div className="App"> <h1>{props.caption}</h1> <input onKeyUp={keyPressHandler} /> <Canvas value={deferredValue} /> </div> ); }
Canvas.js
import React from "react"; const CANVAS_SIZE = 70; const generateRandomColor = () => { var letters = "0123456789ABCDEF"; var color = "#"; for (var i = 0; i < 6; i++) { color += letters[Math.floor(Math.random() * 16)]; } return color; }; const createCanvas = (rows, columns) => { let array = []; for (let i = 0; i < rows; i++) { let row = []; for (let j = 0; j < columns; j++) { row.push(0); } array.push(row); } return array; }; //This is the square with the pixels const drawCanvas = value => { const canvas = createCanvas(CANVAS_SIZE, CANVAS_SIZE); return canvas.map((row, rowIndex) => { let cellsArrJSX = row.map((cell, cellIndex) => { let key = rowIndex + "-" + cellIndex; return ( <div style={{ backgroundColor: generateRandomColor() }} className="cell" key={"cell-" + key} /> ); }); return ( <div key={"row-" + rowIndex} className="canvas-row"> {cellsArrJSX} </div> ); }); }; export const Canvas = ({ value }) => { return ( <div> <h2 style={{ minHeight: 30 }}>{value}</h2> <div className="canvas">{drawCanvas(value)}</div> </div> ); };
Index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /> <meta name="theme-color" content="#000000" /> <title>React App Concurrent Mode</title> </head> <body> <noscript> You need to enable JavaScript to run this app. </noscript> <div id="container"> <div id="root" class="column"></div> <div id="root-concurrent" class="column"></div> </div> </body> </html>
運行示例
讓我們看看我們的代碼。我們看到的第一個屏幕是初始屏幕。使用傳統或塊渲染是現在React 的做法。可中斷渲染是並發模式的測試功能。我們先看看傳統的渲染工作。
像素畫佈在每次擊鍵時重新渲染。在傳統渲染中,整個 UI 會在每次擊鍵時暫停,直到它可以重新渲染屏幕。在此期間,即使我們繼續打字,用戶輸入不會更新。
下圖顯示可中斷渲染。在可中斷渲染中,用戶可以繼續輸入。在為每次擊鍵並行重新渲染畫佈時,UI 不會停止或停止。
重新渲染完成後,React 會更新 UI。雖然在靜態截圖中很難看到,但我們可以看到網格在變化,但用戶仍然可以打字而不會出現 UI 卡頓的情況。
總結
在本文中,我們研究瞭 React 的測試並發功能和 Suspense。使用並發模式,React.js 始終保持用戶界面響應。它將應用程序的任務分解為更小的塊,並允許對用戶界面任務進行優先級排序。因此,此模式可提供更流暢和無縫的用戶體驗,並提高應用程序的整體性能。
結合並發模式,Suspense 允許用戶界面保持響應。同時,數據獲取等繁重耗時的任務可以並行完成,從而提供整體無縫體驗。
有關並發模式的完整詳細信息可在 React 官方文檔中瞭解。
隨著React版本的改進, React框架越來越被更多的中國前端開發者所熟知並且廣泛應用到他們的項目開發中。是繼續Vue.js 後又一備受歡迎的前端主流框架,現在也因此衍生除瞭很多支持與React框架集成的功能工具, 如前端報表ActiveReportsJS控件,提供瞭與 React 直接集成的在線編輯器和報表展示工具,完善前端的數據展示功能。
到此這篇關於React 並發功能體驗-前端的並發模式已經到來的文章就介紹到這瞭,更多相關React 並發功能內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 一文搞懂 React 18 中的 useTransition() 與 useDeferredValue()
- React Suspense前後端IO異步操作處理
- 提高React界面性能的十個技巧
- 詳解React Hooks是如何工作的
- React創建組件的的方式匯總