React 并發(fā)功能體驗(yàn)(前端的并發(fā)模式)
React 是一個(gè)開(kāi)源 JavaScript 庫(kù),開(kāi)發(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ā)布,這些功能已提供給開(kāi)發(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 開(kāi)始渲染某些東西,無(wú)法中斷直到運(yùn)行完成。React 開(kāi)發(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)品性能。開(kāi)發(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í)開(kāi)始渲染。同時(shí)顯示占位符。這種組合產(chǎn)生了更流暢的UI體驗(yàn)。
Suspense 和 懶加載組件
React.lazy是一個(gè)新功能,它使React.js能夠延遲加載組件。懶加載意味著僅在需要時(shí)才加載組件(檢索和呈現(xiàn)它們的代碼)。他們會(huì)優(yōu)先考慮最關(guān)鍵的用戶界面組件。React開(kāi)發(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ā)模式開(kāi)發(fā)的中間階段
- 并發(fā)模式
阻塞模式是使用createBlockingRoot 調(diào)用來(lái)替換createRoot 調(diào)用,在并發(fā)模式的開(kāi)發(fā)情況下,阻塞模式為開(kāi)發(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的畫(huà)布上隨機(jī)分布像素并包含一個(gè)搜索框,每次用戶點(diǎn)擊搜索框時(shí)候,畫(huà)布會(huì)重新渲染自己。
即使UI 界面無(wú)法在并發(fā)模式下渲染,用戶輸入也不會(huì)停止更新。像素畫(huà)布在處理完成后重新渲染。在傳統(tǒng)模式下,快速鍵入時(shí),UI 會(huì)停止,有時(shí)會(huì)在再次渲染畫(huà)布之前停止。用戶輸入也會(huì)停止并且不會(huì)更新。
構(gòu)建像素應(yīng)用程序的主要文件是 canvas.js。我們還制作了一個(gè)輸入框,用戶可以在其中輸入任何內(nèi)容。每次按下一個(gè)鍵都會(huì)重新渲染像素畫(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)的渲染工作。

像素畫(huà)布在每次擊鍵時(shí)重新渲染。在傳統(tǒng)渲染中,整個(gè) UI 會(huì)在每次擊鍵時(shí)暫停,直到它可以重新渲染屏幕。在此期間,即使我們繼續(xù)打字,用戶輸入不會(huì)更新。
下圖顯示可中斷渲染。在可中斷渲染中,用戶可以繼續(xù)輸入。在為每次擊鍵并行重新渲染畫(huà)布時(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ó)前端開(kāi)發(fā)者所熟知并且廣泛應(yīng)用到他們的項(xiàng)目開(kāi)發(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如何修改循環(huán)數(shù)組對(duì)象的數(shù)據(jù)
這篇文章主要介紹了react如何修改循環(huán)數(shù)組對(duì)象的數(shù)據(jù)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12
React踩坑之a(chǎn)ntd輸入框rules中的required=true問(wèn)題
這篇文章主要介紹了React踩坑之a(chǎn)ntd輸入框rules中的required=true問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06
從零搭建react+ts組件庫(kù)(封裝antd)的詳細(xì)過(guò)程
這篇文章主要介紹了從零搭建react+ts組件庫(kù)(封裝antd),實(shí)際上,代碼開(kāi)發(fā)過(guò)程中,還有很多可以輔助開(kāi)發(fā)的模塊、流程,本文所搭建的整個(gè)項(xiàng)目,我都按照文章一步一步進(jìn)行了git提交,開(kāi)發(fā)小伙伴可以邊閱讀文章邊對(duì)照git提交一步一步來(lái)看2022-05-05
create-react-app開(kāi)發(fā)常用配置教程
這篇文章主要為大家介紹了create-react-app開(kāi)發(fā)常用配置教程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06
React框架快速實(shí)現(xiàn)簡(jiǎn)易的Markdown編輯器
這篇文章主要為大家介紹了使用React框架實(shí)現(xiàn)簡(jiǎn)易的Markdown編輯器,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-04-04
React18+TS通用后臺(tái)管理系統(tǒng)解決方案落地實(shí)戰(zhàn)示例
這篇文章主要為大家介紹了React18+TS通用后臺(tái)管理系統(tǒng)解決方案落地實(shí)戰(zhàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08
詳解React中父子組件數(shù)據(jù)傳遞和修改的方式及原理
這篇文章主要為大家詳細(xì)介紹了React中父子組件數(shù)據(jù)傳遞和修改的方式及原理,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-04-04
React.memo函數(shù)中的參數(shù)示例詳解
這篇文章主要為大家介紹了React.memo函數(shù)中的參數(shù)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09

