React 并發(fā)功能體驗(yàn)(前端的并發(fā)模式)
React 是一個(gè)開源 JavaScript 庫(kù),開發(fā)人員使用它來(lái)創(chuàng)建基于 Web 和移動(dòng)的應(yīng)用程序,并且支持構(gòu)建交互式用戶界面和 UI 組件。React 是由 Facebook 軟件工程師 Jordan Walke 創(chuàng)建,React 的第一個(gè)版本在七年前問(wèn)世,現(xiàn)在,F(xiàn)acebook 負(fù)責(zé)維護(hù)。React框架自首次發(fā)布以來(lái),React 的受歡迎程度直線飆升,熱度不減。
2020 年 10 月,React 17 發(fā)布了,但令人驚訝的是——“零新功能”。當(dāng)然,這并不是真的表示沒(méi)有任何新添加的功能,讓廣大程序員使用者興奮。事實(shí)上,這個(gè)版本為我們帶來(lái)了很多重大功能的升級(jí)及16版本的bug修復(fù),并推出了:Concurrent Mode 和Suspense。
雖然這兩個(gè)功能尚未正式發(fā)布,這些功能已提供給開發(fā)人員進(jìn)行測(cè)試。一旦發(fā)布,它們將改變 React 呈現(xiàn)其 UI 的方式,從而達(dá)到雙倍提高性能和用戶體驗(yàn)。
簡(jiǎn)要說(shuō)明, Concurrent Mode 和Suspense 可以使用戶無(wú)縫處理數(shù)據(jù)加載,加載狀態(tài),用戶界面操作更加平滑和無(wú)縫切換。 在Concurrent Mode 下,React可以暫停高消耗的,非緊急的組件的渲染,并聚焦在更加緊迫的任務(wù)處理,如UI 渲染,始終保持應(yīng)用為可響應(yīng)式,避免白屏,卡頓等現(xiàn)象。
本文主要分享深入了解Concurrent Mode 和Suspense 模式下的數(shù)據(jù)提取功能。
為什么需要 Concurrent Mode?
眾所周知,JavaScript 框架或庫(kù)是單線程的工作。因此,當(dāng)一個(gè)代碼塊運(yùn)行時(shí),其余的塊必須等待執(zhí)行。無(wú)法并發(fā)執(zhí)行多線程工作。界面渲染也是一樣的。
一旦 React 開始渲染某些東西,無(wú)法中斷直到運(yùn)行完成。React 開發(fā)人員將這種渲染稱為“阻塞渲染”。 這種阻塞渲染會(huì)創(chuàng)建一個(gè)不穩(wěn)定的用戶界面,并且隨時(shí)可能停止響應(yīng)。
具體問(wèn)題
假如,我們需要顯示一個(gè)很長(zhǎng)的可選列表用于過(guò)濾產(chǎn)品的應(yīng)用程序。我們使用搜索框用于過(guò)濾記錄,設(shè)計(jì)方案是當(dāng)用戶點(diǎn)擊搜索按鈕后,用戶界面需要重新刷新列出相關(guān)聯(lián)的數(shù)據(jù)。
如果列表過(guò)長(zhǎng),數(shù)據(jù)過(guò)多,UI“卡頓”,即渲染對(duì)用戶可見(jiàn)。這種卡頓也會(huì)大大降低產(chǎn)品性能。開發(fā)人員可以使用一些技術(shù),如節(jié)流和防抖,這些技術(shù)會(huì)有一定幫助,但不是完美的解決方案。
節(jié)流限制特定函數(shù)被調(diào)用的次數(shù)。使用節(jié)流,我們可以避免重復(fù)調(diào)用昂貴和耗時(shí)的API或函數(shù)。這個(gè)過(guò)程能夠提高性能,尤其是在用戶界面上呈現(xiàn)信息。
防抖會(huì)在預(yù)定的時(shí)間內(nèi)忽略對(duì)函數(shù)的調(diào)用。函數(shù)調(diào)用僅在經(jīng)過(guò)預(yù)定時(shí)間后進(jìn)行。
下圖描述了卡頓現(xiàn)象:
在等待非緊急 API 調(diào)用完成時(shí),UI 卡頓,從而阻止呈現(xiàn)用戶界面。解決方案是使用并發(fā)模式進(jìn)行可中斷渲染。
無(wú)中斷渲染
通過(guò)可中斷渲染,React.js 在處理和重新渲染列表時(shí)不會(huì)阻塞 UI。它通過(guò)暫?,嵥榈墓ぷ?、更新 DOM 并確保 UI 不會(huì)卡頓,使 React.js 更加細(xì)化。React 使用用戶輸入并行更新或重繪輸入框。React 使用用戶輸入并重繪輸入框并行執(zhí)行。它還更新內(nèi)存中的列表。React 完成更新后,它會(huì)更新 DOM 并在用戶的顯示器上重新呈現(xiàn)列表。本質(zhì)上,無(wú)中斷渲染使 React 能夠“多任務(wù)”。此功能提供了更流暢的 UI 體驗(yàn)。
并發(fā)模式
并發(fā)模式是一組功能,可幫助 React 應(yīng)用程序保持響應(yīng)并平滑地適應(yīng)用戶的設(shè)備和網(wǎng)絡(luò)速度能力。并發(fā)模式將其擁有的任務(wù)劃分為更小的塊。 React 的調(diào)度程序可以挑選并選擇要執(zhí)行的作業(yè)。作業(yè)的調(diào)度取決于它們的優(yōu)先級(jí)。通過(guò)對(duì)任務(wù)進(jìn)行優(yōu)先級(jí)排序,它可以停止瑣碎或不緊急的事情,或者進(jìn)一步推動(dòng)它們。 React 始終將用戶界面更新和渲染放在首位。
使用并發(fā)模式,我們可以:
- 控制首次渲染過(guò)程
- 優(yōu)先處理渲染過(guò)程
- 暫停和恢復(fù)組件的渲染
- 緩存和優(yōu)化組件的運(yùn)行時(shí)渲染
- 隱藏顯示內(nèi)容直到需要展示時(shí)
隨著 UI 渲染,并發(fā)模式改進(jìn)了對(duì)傳入數(shù)據(jù)的響應(yīng),懶加載控件,異步處理過(guò)程。并發(fā)模式保證了用戶界面始終處于激活狀態(tài),并且持續(xù)在后臺(tái)更新數(shù)據(jù),并發(fā)模式也始終使用React 的兩個(gè)鉤掛:useTransition
和useDeferredValue
使用useDeferredValue Hook
useDeferredValue Hook
的定義如下:
const deferredValue = useDeferredValue(value, { timeoutMs: <some value> });
此命令設(shè)置值在timeoutMs
中設(shè)置的時(shí)間后“滯后”。 用戶界面是必須立即更新還是必須等待數(shù)據(jù),該命令使用戶界面保持激活狀態(tài)和響應(yīng)性,該Hook避免了 UI 卡頓,并始終保持用戶界面響應(yīng),以保持獲取數(shù)據(jù)滯后的較小成本。
使用 Transition Hook
useTransition Hook
是React
中主要用于掛起的Hook
,假設(shè)這樣的場(chǎng)景下:其中有一個(gè)帶有用戶名按鈕的網(wǎng)頁(yè)。只需點(diǎn)擊一個(gè)按鈕,網(wǎng)頁(yè)就會(huì)在屏幕上顯示用戶的詳細(xì)信息。
假設(shè)用戶首先單擊一個(gè)按鈕,然后單擊下一個(gè)。屏幕要么變成空白,要么我們?cè)谄聊簧峡吹揭粋€(gè)微調(diào)器。如果獲取詳細(xì)信息花費(fèi)的時(shí)間太長(zhǎng),用戶界面可能會(huì)凍結(jié)。
useTransition
方法返回兩個(gè)Hook
的值:startTransition
和 isPending
。定義的語(yǔ)法如下:
const [startTransition, isPending] = useTransition({ timeoutMs: 3000 });
startTransition
定義的語(yǔ)法:
<button disabled={isPending} startTransition(() => { <fetch Calls> }); </button> {isPending? " Loading...": null}
使用 useTransition
鉤子,React.js
繼續(xù)顯示沒(méi)有用戶詳細(xì)信息的用戶界面,直到用戶詳細(xì)信息準(zhǔn)備好,但 UI 是響應(yīng)式的。React
優(yōu)先考慮用戶界面,以在并行獲取數(shù)據(jù)時(shí)保持響應(yīng)。
為獲取數(shù)據(jù)的Suspense
Suspense
是React
與并發(fā)模式一起引入的另一個(gè)實(shí)驗(yàn)性功能。Suspense
使組件能夠在渲染前等待一段預(yù)定的時(shí)間。
Suspense
的主要作用是從組件異步讀取數(shù)據(jù),而無(wú)需擔(dān)心數(shù)據(jù)的來(lái)源。Suspense
最適合延遲加載的概念。Suspense
允許數(shù)據(jù)獲取庫(kù)通知React
數(shù)據(jù)組件是否可以使用。在必要的組件準(zhǔn)備就緒之前,React
不會(huì)更新 UI。
使用Suspense
的好處:
1.數(shù)據(jù)獲取庫(kù)和React
組件之間的集成
2.控制視覺(jué)加載狀態(tài)
3.避免競(jìng)爭(zhēng)條件
Spinner
組件的基本語(yǔ)法如下:
import Spinner from './Spinner'; <Suspense fallback={<Spinner />}> <SomeComponent /> </Suspense>
Concurrent Mode
中使用的Suspense
允許耗時(shí)的組件在等待數(shù)據(jù)的同時(shí)開始渲染。同時(shí)顯示占位符。這種組合產(chǎn)生了更流暢的UI體驗(yàn)。
Suspense 和 懶加載組件
React.lazy
是一個(gè)新功能,它使React.js
能夠延遲加載組件。懶加載意味著僅在需要時(shí)才加載組件(檢索和呈現(xiàn)它們的代碼)。他們會(huì)優(yōu)先考慮最關(guān)鍵的用戶界面組件。React
開發(fā)人員建議將懶加載組件包裝在Suspense
組件中。
這樣做可確保組件在渲染時(shí)不會(huì)出現(xiàn)“不良狀態(tài)”。用戶界面在整個(gè)過(guò)程中保持響應(yīng),并帶來(lái)更流暢的用戶體驗(yàn)。
啟用并發(fā)模式
要啟用并發(fā)模式,請(qǐng)安裝最新的測(cè)試版本。安裝 React 的先決條件是節(jié)點(diǎn)數(shù)據(jù)包管理器 (npm)。要安裝測(cè)試版本,請(qǐng)執(zhí)行以下命令:
npm install react@experimental react-dom@experimental
要測(cè)試是否設(shè)置了測(cè)試版本,請(qǐng)創(chuàng)建一個(gè)示例 React 應(yīng)用程序。沒(méi)有測(cè)試功能的渲染代碼如下:
import * as React from 'react'; import { render } from 'react-dom'; render(<App />, document.getElementById('root'));
并發(fā)模式的,具體代碼如下:
import * as React from 'react'; import { createRoot } from 'react-dom'; createRoot(document.getElementById('root')).render(<App />);
這將為整個(gè)應(yīng)用程序啟用并發(fā)模式。React 將渲染調(diào)用分為兩部分:
- 創(chuàng)建根元素
- 使用渲染調(diào)用
目前,React 計(jì)劃維護(hù)三種模式:
- 傳統(tǒng)模式是向后兼容的傳統(tǒng)或當(dāng)前模式
- 阻塞模式是并發(fā)模式開發(fā)的中間階段
- 并發(fā)模式
阻塞模式是使用createBlockingRoot 調(diào)用來(lái)替換createRoot 調(diào)用,在并發(fā)模式的開發(fā)情況下,阻塞模式為開發(fā)者提供了機(jī)會(huì)來(lái)修復(fù)bug或解決問(wèn)題。
React 官方文檔中也說(shuō)明了每種模式支持的功能:
示例應(yīng)用:
本文也創(chuàng)建了一個(gè)測(cè)試程序來(lái)驗(yàn)證并發(fā)模式和其他模式的用法和效果。本文以像素應(yīng)用為例在150*150的畫布上隨機(jī)分布像素并包含一個(gè)搜索框,每次用戶點(diǎn)擊搜索框時(shí)候,畫布會(huì)重新渲染自己。
即使UI 界面無(wú)法在并發(fā)模式下渲染,用戶輸入也不會(huì)停止更新。像素畫布在處理完成后重新渲染。在傳統(tǒng)模式下,快速鍵入時(shí),UI 會(huì)停止,有時(shí)會(huì)在再次渲染畫布之前停止。用戶輸入也會(huì)停止并且不會(huì)更新。
構(gòu)建像素應(yīng)用程序的主要文件是 canvas.js。我們還制作了一個(gè)輸入框,用戶可以在其中輸入任何內(nèi)容。每次按下一個(gè)鍵都會(huì)重新渲染像素畫布。
代碼示例: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>
運(yùn)行示例
讓我們看看我們的代碼。我們看到的第一個(gè)屏幕是初始屏幕。使用傳統(tǒng)或塊渲染是現(xiàn)在React 的做法??芍袛噤秩臼遣l(fā)模式的測(cè)試功能。我們先看看傳統(tǒng)的渲染工作。
像素畫布在每次擊鍵時(shí)重新渲染。在傳統(tǒng)渲染中,整個(gè) UI 會(huì)在每次擊鍵時(shí)暫停,直到它可以重新渲染屏幕。在此期間,即使我們繼續(xù)打字,用戶輸入不會(huì)更新。
下圖顯示可中斷渲染。在可中斷渲染中,用戶可以繼續(xù)輸入。在為每次擊鍵并行重新渲染畫布時(shí),UI 不會(huì)停止或停止。
重新渲染完成后,React 會(huì)更新 UI。雖然在靜態(tài)截圖中很難看到,但我們可以看到網(wǎng)格在變化,但用戶仍然可以打字而不會(huì)出現(xiàn) UI 卡頓的情況。
總結(jié)
在本文中,我們研究了 React 的測(cè)試并發(fā)功能和 Suspense。使用并發(fā)模式,React.js 始終保持用戶界面響應(yīng)。它將應(yīng)用程序的任務(wù)分解為更小的塊,并允許對(duì)用戶界面任務(wù)進(jìn)行優(yōu)先級(jí)排序。因此,此模式可提供更流暢和無(wú)縫的用戶體驗(yàn),并提高應(yīng)用程序的整體性能。
結(jié)合并發(fā)模式,Suspense 允許用戶界面保持響應(yīng)。同時(shí),數(shù)據(jù)獲取等繁重耗時(shí)的任務(wù)可以并行完成,從而提供整體無(wú)縫體驗(yàn)。
有關(guān)并發(fā)模式的完整詳細(xì)信息可在 React 官方文檔中了解。
隨著React版本的改進(jìn), React框架越來(lái)越被更多的中國(guó)前端開發(fā)者所熟知并且廣泛應(yīng)用到他們的項(xiàng)目開發(fā)中。是繼續(xù)Vue.js 后又一備受歡迎的前端主流框架,現(xiàn)在也因此衍生除了很多支持與React框架集成的功能工具, 如前端報(bào)表ActiveReportsJS控件,提供了與 React 直接集成的在線編輯器和報(bào)表展示工具,完善前端的數(shù)據(jù)展示功能。
到此這篇關(guān)于React 并發(fā)功能體驗(yàn)-前端的并發(fā)模式已經(jīng)到來(lái)的文章就介紹到這了,更多相關(guān)React 并發(fā)功能內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
react數(shù)據(jù)管理機(jī)制React.Context源碼解析
這篇文章主要為大家介紹了react數(shù)據(jù)管理機(jī)制React.Context源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11React實(shí)現(xiàn)文件上傳和斷點(diǎn)續(xù)傳功能的示例代碼
這篇文章主要為大家詳細(xì)介紹了React實(shí)現(xiàn)文件上傳和斷點(diǎn)續(xù)傳功能的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-02-02React Antd中如何設(shè)置表單只輸入數(shù)字
這篇文章主要介紹了React Antd中如何設(shè)置表單只輸入數(shù)字問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06詳解使用WebPack搭建React開發(fā)環(huán)境
這篇文章主要介紹了詳解使用WebPack搭建React開發(fā)環(huán)境,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08ReactRouterV6如何獲取當(dāng)前路由參數(shù)
這篇文章主要介紹了ReactRouterV6如何獲取當(dāng)前路由參數(shù)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03