" />

欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

React渲染機(jī)制及相關(guān)優(yōu)化方案

 更新時(shí)間:2023年07月13日 10:41:09   作者:大橘為重¨  
這篇文章主要介紹了react中的渲染機(jī)制以及相關(guān)的優(yōu)化方案,內(nèi)容包括react渲染步驟、concurrent機(jī)制以及產(chǎn)生作用的機(jī)會,簡單模擬實(shí)現(xiàn) concurrent mode,基于作業(yè)調(diào)度優(yōu)先級的思路進(jìn)行項(xiàng)目優(yōu)化的兩個(gè)hooks,感興趣的小伙伴跟著小編一起來看看吧

一、react渲染步驟

  • 準(zhǔn)備階段(Prepare Phase)
    在準(zhǔn)備階段,React 會收集組件的依賴關(guān)系,建立組件樹的數(shù)據(jù)結(jié)構(gòu),確定組件的更新優(yōu)先級,并生成用于渲染的工作單元。

  • 計(jì)算階段(Compute Phase)
    在計(jì)算階段,React 會根據(jù)組件的更新優(yōu)先級和調(diào)度策略,將工作單元分成多個(gè)批次進(jìn)行處理。每個(gè)批次都會執(zhí)行一小部分工作單元,以保證用戶界面的響應(yīng)性。

  • 渲染階段(Render Phase)
    在渲染階段,React 會根據(jù)工作單元的類型和優(yōu)先級,執(zhí)行相應(yīng)的渲染操作。這包括創(chuàng)建新的虛擬 DOM 節(jié)點(diǎn)、更新現(xiàn)有的虛擬 DOM 節(jié)點(diǎn),以及卸載不再需要的組件。

  • 提交階段(Commit Phase)
    在提交階段,React 會將更新后的虛擬 DOM 節(jié)點(diǎn)映射到實(shí)際的 DOM,更新用戶界面。這個(gè)階段還會執(zhí)行一些副作用操作,如執(zhí)行useEffect。

二、concurrent機(jī)制以及產(chǎn)生作用的機(jī)會

注:React 的并發(fā)模式(Concurrency Mode)是一種用于處理大型和復(fù)雜應(yīng)用程序的特性,旨在提高應(yīng)用程序的性能和響應(yīng)能力。解決react中狀態(tài)更新就會觸發(fā)該組件及該組件下所有子組件無腦更新而引發(fā)的性能問題;同時(shí)提供部分控制作業(yè)調(diào)度優(yōu)先級的能力給開發(fā)者使用

  • 在傳統(tǒng)的 React 渲染模式中,更新操作是同步進(jìn)行的,即在進(jìn)行更新時(shí),會立即進(jìn)行組件的重新渲染,可能會阻塞主線程,導(dǎo)致頁面響應(yīng)變慢或失去響應(yīng)出現(xiàn)掉幀問題。

  • 而concurrent mode通過引入一種新的調(diào)度算法和優(yōu)先級機(jī)制,將更新操作劃分為多個(gè)優(yōu)先級,使得 React 可以更好地管理和分配任務(wù),以實(shí)現(xiàn)更平滑的用戶體驗(yàn)。

  • concurrent mode主要具備以下幾個(gè)特性:異步渲染、優(yōu)先級調(diào)度、遞增式渲染

補(bǔ)充:concurrent mode 主要工作在渲染流程的 Compute Phase 及 Render Phase,因?yàn)樗鼈兪羌兇獾?JS 計(jì)算意味著可以被拆分,而 commit 階段由于帶有 DOM 更新,不可能 DOM 變更到一半中斷,因此必須一次性執(zhí)行完成

1. 優(yōu)先級調(diào)度:

concurrent mode 通過對任務(wù)進(jìn)行優(yōu)先級劃分,React 可以根據(jù)優(yōu)先級動(dòng)態(tài)地分配和重新分配任務(wù)。基于此React 可以更好地響應(yīng)用戶交互和其他高優(yōu)先級的任務(wù),同時(shí)提供了 “useDeferredValue” 、“useTransition” 兩個(gè)hooks用于調(diào)度作業(yè)任務(wù)的優(yōu)先級。

2. 遞增式渲染:

1)concurrent mode 下的渲染是逐步進(jìn)行的,React 將大量需要重新渲染的組件的工作基于時(shí)間片的理念劃分為多個(gè)小片段工作,在瀏覽器的每一幀的空閑時(shí)間中去執(zhí)行這些渲染工作,而不是一下子全部直接執(zhí)行,這樣有效的避免了掉幀情況的出現(xiàn)。

2)這里也就說明了為什么React官方說 componentWillMount 可能被調(diào)用多次的原因,正是因?yàn)榈蛢?yōu)先級任務(wù)的 render 階段可能被重復(fù)的中斷和重新執(zhí)行,而 componentWillMount 就包含在 render 階段中。

注意:工作拆分的最小單元應(yīng)該是一個(gè)fiber節(jié)點(diǎn),當(dāng)某個(gè)fiber節(jié)點(diǎn)本身的計(jì)算就十分巨大時(shí)依然會導(dǎo)致卡幀,不過我們可以通過調(diào)整工作的優(yōu)先級使得用戶的體驗(yàn)是平滑的

三、簡單模擬實(shí)現(xiàn) concurrent mode 的遞增式渲染

  • 下面使用 requestIdleCallback 函數(shù)模擬時(shí)間片,在每一幀的空閑時(shí)間進(jìn)行js計(jì)算從而達(dá)到遞增式渲染的效果

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="index.js"></script>
</head>
<body>
    <div id="root"></div>
    <script>
        // 調(diào)用render提供掛載容器 "root"
        render(document.getElementById('root'))
    </script>
</body>
</html>

index.js

// 頁面需要渲染的組件
function Counter() {
    return {
        type: 'span',
        value: 'hello world',
        next: {
            type: 'p',
            value: 'hello LiHua'
        }
    }
}
const CounterElementDescriptors = {
    type: 'Function',
    fn: Counter
}
// 記錄當(dāng)前工作
let presentWork = null
// 記錄根元素
let rootElementDescriptor = null    
// 記錄掛載容器 
let elementsContainer = null    
// 處理單元任務(wù)
function performUnitOfWork(deadline) {
    // 判斷當(dāng)前是否還有待執(zhí)行任務(wù)
    if (presentWork == null) return commitRoot(rootElementDescriptor)
    // 當(dāng)前幀超時(shí),調(diào)用 requestIdleCallback 把任務(wù)推到下一幀空閑時(shí)間執(zhí)行
    if (deadline.didTimeout) return requestIdleCallback(executeWorkLoop)
    // 若是組件則處理依賴關(guān)系、若是元素則生成真實(shí)dom
    if (presentWork.type === "Function") {
        rootElementDescriptor = presentWork
        const firstChildren = presentWork.fn()
        firstChildren.parent = presentWork
        presentWork.children = firstChildren
        presentWork = firstChildren
        performUnitOfWork(deadline)
    } else {
        const dom = document.createElement(presentWork.type)
        dom.innerHTML = presentWork.value
        presentWork.dom = dom
        presentWork = presentWork.next
        performUnitOfWork(deadline)
    }
}
// 控制循環(huán)執(zhí)行工作
function executeWorkLoop(deadline) {
    performUnitOfWork(deadline)
}
// 提供render函數(shù),用于獲取掛載容器和開始渲染計(jì)算工作
function render(element) {
    elementsContainer = element
    presentWork = CounterElementDescriptors
    requestIdleCallback(executeWorkLoop)
}
// 模擬commit階段
function commitRoot(rootElement) {
    let renderCHildrenElements = rootElement.children
    do {
        elementsContainer.appendChild(renderCHildrenElements.dom)
        renderCHildrenElements = renderCHildrenElements.next
    }while(renderCHildrenElements)
}

四、與優(yōu)先級調(diào)度有關(guān)的兩個(gè)hooks

1. useTransition

官方解釋:useTransition 是一個(gè)讓你在不阻塞 UI 的情況下來更新狀態(tài)的 React Hook。

  • 通過 useTransition 我們可以將一部分的狀態(tài)更新工作劃分為低優(yōu)先級的異步任務(wù),使它不阻塞主要任務(wù)的執(zhí)行
  • 同時(shí)我們可以依據(jù) useTransition 返回的標(biāo)志狀態(tài)在渲染期間優(yōu)雅地展示加載狀態(tài),從而提高用戶界面的交互體驗(yàn)和流暢性
  • useTransition 主要語法如下:
import { useTransition } from "react";
function TabContainer() {
    // isPending 標(biāo)志,告訴你是否存在待處理的低優(yōu)先級工作。
    // startTransition 函數(shù) 允許你將該部分的狀態(tài)更新標(biāo)記為低優(yōu)先級。
    const [isPending, startTransition] = useTransition();
    function handle() {
        startTransition(() => {
            // 低優(yōu)先級的狀態(tài)更新工作
            {......}
        });
    }
    return (
        {......}
    )
}

2. useDeferredValue

官方解釋:useDeferredValue 是一個(gè) React Hook,可以讓你延遲更新 UI 的某些部分。

  • 通過 useDeferredValue 我們可以將一部分的UI更新工作劃分為低優(yōu)先級的任務(wù),使它不阻塞主要任務(wù)的執(zhí)行
  • useTransition 主要語法如下:
import { useDeferredValue, useState, } from "react";
function TabContainer() {
    const [query, setQuery] = useState('');
    // 定義的 deferredQuery 獲取的是query的延遲版本
    const deferredQuery = useDeferredValue(query);
    function handle(data) {
        setQuery(data)
    }
    return (
    	<>
    		<List listData={deferredQuery} />
    		{ ......}
    	</>
    )
}

3. useTransition 與 useDeferredValue 的區(qū)別

  • useTransition 用于控制過渡狀態(tài),可以在過渡狀態(tài)中執(zhí)行任務(wù),并提供過渡狀態(tài)的布爾值來判斷是否處于過渡狀態(tài)。
  • useDeferredValue 用于延遲某個(gè)值的更新,以避免在渲染過程中處理昂貴的計(jì)算或數(shù)據(jù)獲取,確保界面的流暢性。
  • 雖然它們都與并發(fā)模式相關(guān),但用途和作用略有不同,具體使用哪一個(gè)需要看具體場景。

4. 應(yīng)用場景

1)長列表渲染:當(dāng)渲染大量列表項(xiàng)時(shí),可以對列表項(xiàng)的渲染任務(wù)調(diào)節(jié)為低優(yōu)先級異步任務(wù),以保證用戶界面的響應(yīng)性能。

2)大型表單處理:對于包含大量輸入字段的表單,可以使用合理使用對于hooks將表單提交和驗(yàn)證等任務(wù)進(jìn)行優(yōu)化調(diào)節(jié),以避免阻塞用戶界面。

3)圖片懶加載:當(dāng)頁面中包含大量圖片時(shí),可以使用 useTransition 將圖片的加載劃分為多個(gè)低優(yōu)先級異步任務(wù),在渲染期間逐步加載圖片,以減少對用戶界面的阻塞。

4)異步數(shù)據(jù)加載:當(dāng)頁面中的數(shù)據(jù)需要從后端異步加載時(shí),可以使用 useTransition 將數(shù)據(jù)的加載劃分為多個(gè)異步任務(wù),以保證用戶界面的響應(yīng)性能。

五、一個(gè)小例子

  • 以下以長列表渲染為例子做演示

基礎(chǔ)代碼,未作優(yōu)化處理:

import React, { useCallback, useState } from 'react'
const index: React.FC = () => {
    const [list, setList] = useState<any[]>([])
    const handleSearch = useCallback((value: string) => {
            const newList = []
            for (let i = 0; i < 5000; i++) {
                newList.push(value + '-' + i)
            }
            setList(newList)
    }, [])
    return (
        <>
            <input onChange={(e) => handleSearch(e.target.value)} type='text' />
            <div>
                {list.map(item => <div key={item}>數(shù)據(jù)項(xiàng):{item}</div>)}
            </div>
        </>
    )
}
export default index

當(dāng)我們進(jìn)行持續(xù)的輸入時(shí)是十分的卡頓的,效果如下:

1. 下面使用 useTransition 進(jìn)行優(yōu)化

  • 降低 “setList(newList)” 的優(yōu)先級,使其不阻塞用戶輸入事件的觸發(fā)

代碼修改如下:

import React, { useCallback, useState, useTransition } from 'react'
const index: React.FC = () => {
    const [list, setList] = useState<any[]>([])
    const [isPending, startTransition] = useTransition()
    const handleSearch = useCallback((value: string) => {
        startTransition(() => {
            const newList = []
            for (let i = 0; i < 5000; i++) {
                newList.push(value + '-' + i)
            }
            setList(newList)
        })
    }, [])
    return (
        <>
            <input onChange={(e) => handleSearch(e.target.value)} type='text' />
            <div>
                {isPending? '加載中。。。' : list.map(item => <div key={item}>數(shù)據(jù)項(xiàng):{item}</div>)}
            </div>
        </>
    )
}
export default index

優(yōu)化后效果如下:

2. 使用 useDeferredValue 進(jìn)行優(yōu)化

  • 降低 “列表部分UI” 更新渲染的優(yōu)先級,使其不阻塞用戶輸入事件的觸發(fā)

代碼修改如下:

import React, { memo, useDeferredValue, useState } from 'react'
const Item = ({ text }: any) => {
    return (
        <div>
            數(shù)據(jù)項(xiàng):{text}
        </div>
    )
}
const List = memo(({ inputValue }: { inputValue: string }) => {
    let items = [];
    for (let i = 0; i < 5000; i++) {
        items.push(<Item key={i} text={inputValue + '-' + i} />);
    }
    return (
        <>
            {items}
        </>
    );
})
const index: React.FC = () => {
    const [inputValue, setInputValue] = useState('')
    const deferredInputValue = useDeferredValue(inputValue)
    return (
        <>
            <input value={inputValue} onChange={(e) => setInputValue(e.target.value)} type='text' />
            <List inputValue={deferredInputValue} />
        </>
    )
}
export default index

優(yōu)化后效果如下:

補(bǔ)充:為什么VUE不需要設(shè)計(jì) Concurrent Mode

  • 出于vue響應(yīng)式系統(tǒng)的設(shè)計(jì)實(shí)現(xiàn)思路的不同,也就體現(xiàn)了為什么。

1)在vue中,響應(yīng)式系統(tǒng)通過 proxy 實(shí)現(xiàn)對 render函數(shù) 的依賴收集和觸發(fā)更新,基于追蹤組件依賴的響應(yīng)式數(shù)據(jù)的變化,可以更為精準(zhǔn)的實(shí)現(xiàn)組件的更新,大大避免了不必要的渲染和更新操作,規(guī)避了react中狀態(tài)更新就會觸發(fā)組件及該組件下所有子組件無腦更新的問題。

2)同時(shí)vue的異步更新策略也有助于提高性能和響應(yīng)能力。Vue會在下一個(gè)事件循環(huán)周期中批量更新組件,這樣可以避免頻繁的DOM操作和重復(fù)渲染,提高渲染效率。

3)但vue中暫時(shí)沒有 useTransition 和 useDeferredValue 類似的功能操作,無法調(diào)度控制作業(yè)的優(yōu)先級

以上就是React渲染機(jī)制及相關(guān)優(yōu)化方案的詳細(xì)內(nèi)容,更多關(guān)于React渲染機(jī)制的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • react?express實(shí)現(xiàn)webssh?demo解析

    react?express實(shí)現(xiàn)webssh?demo解析

    這篇文章主要為大家介紹了詳解react?express實(shí)現(xiàn)webssh?demo解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-04-04
  • 解決react組件渲染兩次的問題

    解決react組件渲染兩次的問題

    這篇文章主要介紹了解決react組件渲染兩次的問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • 解決React報(bào)錯(cuò)Expected an assignment or function call and instead saw an expression

    解決React報(bào)錯(cuò)Expected an assignment or funct

    這篇文章主要為大家介紹了React報(bào)錯(cuò)Expected an assignment or function call and instead saw an expression解決方案詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • react-router JS 控制路由跳轉(zhuǎn)實(shí)例

    react-router JS 控制路由跳轉(zhuǎn)實(shí)例

    這篇文章主要介紹了react-router JS 控制路由跳轉(zhuǎn)實(shí)例,react實(shí)現(xiàn)路由可以直接使用react-router。有興趣的可以了解一下
    2017-06-06
  • React?中?setState?的異步操作案例詳解

    React?中?setState?的異步操作案例詳解

    這篇文章主要介紹了React中setState的異步操作案例詳解,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一點(diǎn)點(diǎn)參考價(jià)值,感興趣的小伙伴可以參考一下
    2022-08-08
  • 詳解create-react-app 2.0版本如何啟用裝飾器語法

    詳解create-react-app 2.0版本如何啟用裝飾器語法

    這篇文章主要介紹了詳解create-react-app 2.0版本如何啟用裝飾器語法,cra2.0時(shí)代如何啟用裝飾器語法呢? 我們依舊采用的是react-app-rewired, 通過劫持webpack cofig對象, 達(dá)到修改的目的
    2018-10-10
  • React腳手架搭建的學(xué)習(xí)

    React腳手架搭建的學(xué)習(xí)

    本文主要介紹了React腳手架搭建的學(xué)習(xí),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • react組件中獲取DOM元素的五種方式

    react組件中獲取DOM元素的五種方式

    本文主要介紹了React組件中獲取DOM元素的五種方式:包括使用ref屬性、構(gòu)造器創(chuàng)建全局變量、動(dòng)態(tài)綁定ref屬性、綁定函數(shù)定義全局變量和使用hook語法查找DOM,感興趣的可以了解一下
    2025-01-01
  • React-Router6版本的更新引起的路由用法變化

    React-Router6版本的更新引起的路由用法變化

    本文主要介紹了React-Router6版本的更新引起的路由用法變化,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • react純函數(shù)組件setState更新頁面不刷新的解決

    react純函數(shù)組件setState更新頁面不刷新的解決

    在開發(fā)過程中,經(jīng)常遇到組件數(shù)據(jù)無法更新,本文主要介紹了react純函數(shù)組件setState更新頁面不刷新的解決,感興趣的可以了解一下
    2021-06-06

最新評論