成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

關于React的一切——React的并發悖論

開發 前端
當一個React應用邏輯變得復雜后,「組件Render」花費的時間會顯著增長。如果從「組件Render」到「視圖渲染」期間消耗的時間過長,用戶就會感知到頁面卡頓。

大家好,我卡頌。

當一個React應用邏輯變得復雜后,「組件render」花費的時間會顯著增長。如果從「組件render」到「視圖渲染」期間消耗的時間過長,用戶就會感知到頁面卡頓。

為了解決這個問題,有兩個方法:

  1. 讓「組件render」的過程從同步變為異步,這樣render過程頁面不會卡死。這就是并發更新的原理。
  2. 減少需要render的組件數量,這就是常說的React性能優化。

通常,對于不同類型組件,我們會采取以上不同的方法。比如,對于下面這樣的有耗時邏輯的輸入框,方法1更合適(因為并發更新能減少輸入時的卡頓):

function ExpensiveInput({onChange, value}) {
// 耗時的操作
const cur = performance.now();
while (performance.now() - cur < 20) {}

return <input onChange={onChange} value={value}/>;
}

那么,能不能在整個應用層面同時兼顧這2種方式呢?答案是 —— 不太行。

這是因為,對于復雜應用,并發更新與性能優化通常是相悖的。就是本文要聊的 —— 并發悖論。

從性能優化聊起

對于一個組件,如果希望他非必要時不render,需要達到的基本條件是:props的引用不變。

比如,下面代碼中Child組件依賴fn props,由于fn是內聯形式,所以每次App組件render時引用都會變,不利于Child性能優化:

function App() {
return <Child fn={() => {/* xxx */}}/>
}

為了Child性能優化,可以將fn抽離出來:

const fn = () => {/* xxx */}

function App() {
return <Child fn={fn}/>
}

當fn依賴某些props或者state時,我們需要使用useCallback:

function App({a}) {
const fn = useCallback(() => a + 1, [a]);
return <Child fn={fn}/>
}

類似的,其他類型變量需要用到useMemo。

也就是說,當涉及到性能優化時,React的代碼邏輯會變得復雜(需要考慮引用變化問題)。

當應用進一步復雜,會面臨更多問題,比如:

  • 復雜的useEffect邏輯。
  • 狀態如何共享。

這些問題會與性能優化問題互相疊加,最終導致應用不僅邏輯復雜,性能也欠佳。

性能優化的解決之道

好在,這些問題有個共同的解決方法 —— 狀態管理。

上文我們聊到,對于性能優化,關鍵的問題是 —— 保持props引用不變。

在原生React中,如果a依賴b,b依賴c。那么,當a變化后,我們需要通過各種方法(比如useCallback、useMemo)保持b、c引用的穩定。

做這件事情本身(保持引用不變)對開發者來說就是額外的心智負擔。那么,狀態管理是如何解決這個問題的呢?

答案是:狀態管理庫自己管理所有原始狀態以及派生狀態。

比如:

  • 在Recoil中,基礎狀態類型被稱為Atom,其他派生狀態都是基于Atom組合而來
  • 在Zustand中,基礎狀態都是create方法創建的實例
  • 在Redux中,維護了一個全局狀態,對于需要用到的狀態通過selector從中摘出來

這些狀態管理方案都會自己維護所有的基礎狀態與派生狀態。當開發者從狀態管理庫中引入狀態時,就能最大限度保持props引用不變。

比如,下例用Zustand改造上面的代碼。由于狀態a和依賴a的fn都是由Zustand管理,所以fn的引用始終不變:

const useStore = create(set => ({
a: 0,
fn: () => set(state => ({ a: state.a + 1 })),
}))
function App() {
const fn = useStore(state => state.fn)
return <Child fn={fn}/>
}

并發更新的問題

現在我們知道,性能優化的通用解決途徑是 —— 通過狀態管理庫,維護一套邏輯自洽的外部狀態(這里的「外部」是區別于React自身的狀態),保持引用不變。

但是,這套外部狀態最終一定會轉化為React的內部狀態(再通過內部狀態的變化驅動視圖更新),所以就存在狀態同步時機的問題。即:什么時候將外部狀態與內部狀態同步?

在并發更新之前的React中,這并不是個問題。因為更新是同步、不會被打斷的。所以對于同一個外部狀態,在整個更新過程中都能保持不變。

比如,在如下代碼中,由于List?組件的render?過程不會打斷,所以list在遍歷過程中是穩定的:

function List() {
const list = useStore(state => state.list)
return (
<ul>
{list.map(item => <Item key={item.id} data={item}/>}
</ul>
)
}

但是,對于開啟并發更新的React,更新流程可能中斷,不同的Item組件可能是在中斷前后不同的宏任務中render,傳遞給他們的data props可能并不相同。這就導致同一次更新,同一個狀態(例子中的list)前后不一致的情況。

這種情況被稱為tearing(視圖撕裂)。

可以發現,造成tearing的原因是 —— 外部狀態(狀態管理庫維護的狀態)與React內部狀態的同步時機出問題。

這個問題在當前React中是很難解決的。退而求其次,為了讓這些狀態庫能夠正常使用,React專門出了個hook —— useSyncExternalStore。用于將狀態管理庫觸發的更新都以同步的方式執行,這樣就不會有同步時機的問題。

既然是以同步的方式執行,那肯定沒法并發更新啦~~~

總結

實際上,凡是涉及到「自己維護了一個外部狀態」的庫(比如動畫庫),都涉及到狀態同步的問題,很有可能無法兼容并發更新。

所以,你會更傾向下面哪種選擇呢:

  1. 不care并發更新,以前React怎么用,現在就怎么用。
  2. 根據項目情況,平衡并發更新與性能優化的訴求。
責任編輯:姜華 來源: 魔術師卡頌
相關推薦

2023-09-11 07:36:35

2020-09-11 10:55:10

useState組件前端

2018-11-23 11:17:24

負載均衡分布式系統架構

2021-02-19 23:08:27

軟件測試軟件開發

2021-02-28 09:47:54

軟件架構軟件開發軟件設計

2020-10-14 08:04:28

JavaScrip

2021-05-28 07:12:59

Python閉包函數

2022-08-21 17:35:31

原子多線程

2023-02-10 08:44:05

KafkaLinkedIn模式

2018-01-05 14:23:36

計算機負載均衡存儲

2021-08-09 14:40:02

物聯網IOT智能家居

2023-07-10 10:36:17

人工智能AI

2022-04-02 09:38:00

CSS3flex布局方式

2022-08-17 06:25:19

偽共享多線程

2018-01-17 09:15:52

負載均衡算法

2023-04-12 14:04:48

光纖網絡

2012-12-31 11:22:58

開源開放

2023-10-12 07:06:32

2020-11-17 10:38:40

云計算工具技術

2023-10-12 09:42:44

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产精品一区久久久 | 日韩一级免费电影 | 少妇一区在线观看 | 久久久久一区 | 亚洲免费一区 | 日本成人在线播放 | 日韩一区二区不卡 | 亚洲男女激情 | 神马影院一区二区三区 | 亚洲精品www| 久久中文字幕一区 | 不卡在线视频 | 国产特级毛片aaaaaa喷潮 | 国产精品99久久久久久久vr | 欧美日本免费 | 中文字幕精品一区二区三区精品 | 亚洲啊v在线 | 日韩在线一区二区 | 久久久久久毛片免费观看 | 欧美高清视频一区 | 伊人一区 | 91高清视频在线观看 | 欧美视频二区 | 久久久久久天堂 | 国产精品视频免费看 | 亚洲欧美另类在线观看 | 91超碰caoporn97人人 | 国产视频精品视频 | 91精品国产综合久久精品 | 一区二区三区国产 | 在线啊v| 午夜精品一区二区三区在线观看 | 欧美日韩在线观看一区二区三区 | 日日日干干干 | 狠狠婷婷综合久久久久久妖精 | 国产精品久久在线观看 | h小视频 | 综合伊人| 激情久久网 | 黄色片在线 | 玖玖操 |