日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

理解這個機制,是成為React性能優化高手的關鍵

來源: 責編: 時間:2024-01-16 10:14:01 205觀看
導讀本來是準備優先分享兩個官方定義的 Hook useMemo,useCallback,不過這兩個 hook 本身其實沒有太多探討的空間,他們只是兩個記憶函數,本身并沒有特殊的、更進一步的含義。許多人的困惑往往來源于對于它們兩個過度解讀,認為他

MMR28資訊網——每日最新資訊28at.com

本來是準備優先分享兩個官方定義的 Hook useMemo,useCallback,不過這兩個 hook 本身其實沒有太多探討的空間,他們只是兩個記憶函數,本身并沒有特殊的、更進一步的含義。MMR28資訊網——每日最新資訊28at.com

許多人的困惑往往來源于對于它們兩個過度解讀,認為他們的存在對 React 性能的優化有非常重要的意義。過渡解讀導致了對他們的濫用。在我看過的項目中,有個別優秀前端團隊里的項目規范里,也錯誤抬高了他們的作用,把他們用在了每一個組件里。MMR28資訊網——每日最新資訊28at.com

出現這樣問題的根源就在于對 React 的自身機制理解不夠精準。因此我決定換一個角度去帶大家理解 React 本身的優化機制,從而能夠正確的使用 useMemo 與 useCallback。MMR28資訊網——每日最新資訊28at.com

本文將會從應用層面來為大家分析我們應該怎么做。后續的章節將會從 Fiber 的雙緩存策略開始分享底層的優化機制。MMR28資訊網——每日最新資訊28at.com

一、精簡節點

首先我們要明確一些前置知識。MMR28資訊網——每日最新資訊28at.com

React 在內存中維護了一顆 虛擬 DOM 樹,這顆樹的每一個節點是一個 Fiber,每一個 Fiber 都由 JSX 中的組件解析而來。MMR28資訊網——每日最新資訊28at.com

type Fiber = {  // 用于標記fiber的WorkTag類型,主要表示當前fiber代表的組件類型如FunctionComponent、ClassComponent等  tag: WorkTag,  // ReactElement里面的key  key: null | string,  // ReactElement.type,調用`createElement`的第一個參數  elementType: any,  // The resolved function/class/ associated with this fiber.  // 表示當前代表的節點類型  type: any,  // 表示當前FiberNode對應的element組件實例  stateNode: any,  // 指向他在Fiber節點樹中的`parent`,用來在處理完這個節點之后向上返回  return: Fiber | null,  // 指向自己的第一個子節點  child: Fiber | null,  // 指向自己的兄弟結構,兄弟節點的return指向同一個父節點  sibling: Fiber | null,  index: number,  ref: null | (((handle: mixed) => void) & { _stringRef: ?string }) | RefObject,  // 當前處理過程中的組件props對象  pendingProps: any,  // 上一次渲染完成之后的props  memoizedProps: any,  // 該Fiber對應的組件產生的Update會存放在這個隊列里面  updateQueue: UpdateQueue<any> | null,  // 上一次渲染的時候的state  memoizedState: any,  // 一個列表,存放這個Fiber依賴的context  firstContextDependency: ContextDependency<mixed> | null,  mode: TypeOfMode,  // Effect  // 用來記錄Side Effect  effectTag: SideEffectTag,  // 單鏈表用來快速查找下一個side effect  nextEffect: Fiber | null,  // 子樹中第一個side effect  firstEffect: Fiber | null,  // 子樹中最后一個side effect  lastEffect: Fiber | null,  // 代表任務在未來的哪個時間點應該被完成,之后版本改名為 lanes  expirationTime: ExpirationTime,  // 快速確定子樹中是否有不在等待的變化  childExpirationTime: ExpirationTime,  // fiber的版本池,即記錄fiber更新過程,便于恢復  alternate: Fiber | null,}

state 的每次變化,都會引發整棵樹的前后對比,從而導致許多組件重新執行。這也是 React 性能消耗的主要成本。但是 React 內部采用緩存機制和優秀的 Diff 算法極大的減少了這里的成本,后續我們會詳細介紹這兩個機制。MMR28資訊網——每日最新資訊28at.com

這里我要重點介紹的是,在使用中,我們可以通過減小這顆 Fiber tree 的方式來達到性能優化的目的。只要 Fiber tree 足夠小,diff 的成本就會非常的低。MMR28資訊網——每日最新資訊28at.com

例如,我們有一個非常大的巨石項目,當我們路由切換的時候,會直接刪掉前一個頁面的所有內容,只渲染新頁面的內容,那么,雖然隨著訪問頁面的數量越來越多,緩存在全局狀態管理器中的數據越來越復雜,但是 Fiber tree 的大小其實并沒有變得越來越大,依然維持在一個頁面的量級,此時的 diff 壓力跟一個小型項目沒有什么區別。通過這種手段,我們可以輕松保持一個巨石項目的高性能。MMR28資訊網——每日最新資訊28at.com

落實到具體的頁面上,特別是在一些管理系統里,許多開發者喜歡在在列表頁中,維護一個內容超級復雜的彈窗組件,彈窗的內容是列表的詳情。此時,彈窗內容和列表內容同時存在,從而導致了 Fiber tree 的龐大。MMR28資訊網——每日最新資訊28at.com

從交互上,我們可以將復雜的彈窗內容移植到一個新的詳情頁,就能極大的緩解 diff 壓力。MMR28資訊網——每日最新資訊28at.com

在某些項目中,一個詳情頁有幾百條表單需要填寫。我們可以通過分步驟的方式,把這幾百個表單項切分到不同的步驟里,從而讓同時渲染出來的表單項大量減少,性能也會有很大的提高。MMR28資訊網——每日最新資訊28at.com

總的來說,只要我們把 Fiber 節點數量控制在一定范圍內,React 都能保持一個非常高的性能。因此大多數情況下,我們并不需要做額外的性能優化。MMR28資訊網——每日最新資訊28at.com

二、比較方式

由于大量的 re-render 存在,我們很容易能想到一個優化策略,在 diff 過程中,當我比較之后發現有的節點并沒有發生任何變化,那么我們就可以跳過該組件的 re-render,從而提高性能。MMR28資訊網——每日最新資訊28at.com

而要讓這個優化想法落地,我們就必須了解內部的比較規則,首先要考慮的第一個問題就是。MMR28資訊網——每日最新資訊28at.com

如何知道一個組件是否發生了變化

一個 React 組件是否發生了變化由三個因素決定。MMR28資訊網——每日最新資訊28at.com

  • props
  • state
  • context

這三個因素中的任何一個發生了變化,組件都會認為自己應該發生變化。state 和 context 都是不可變數據,而且由于是我們主動調用 dispatch 去觸發他們發生改變,因此 state 和 context 的變化一般不會對我們造成理解上的困擾。MMR28資訊網——每日最新資訊28at.com

最麻煩的是 props。MMR28資訊網——每日最新資訊28at.com

React 組件的每次執行,都會傳入新的 props 對象,雖然內容可能一樣,但是在內存中卻已經發生了變化。MMR28資訊網——每日最新資訊28at.com

function Child(props) {}// 執行一次傳入新的對象Child({})// 執行一次傳入新的對象Child({})

與 state 不一樣的是,props 并沒有緩存在別的地方,因此,一個組件 的 props 哪怕什么都沒有變化,比較的結果也是 false。MMR28資訊網——每日最新資訊28at.com

var preProps = {}var curProps = {}preProps === curProps // false
var preProps = { name: 'Jake' }var curProps = { name: 'Jake' }preProps === curProps // false

呵!沒想到吧。MMR28資訊網——每日最新資訊28at.com

也就是說,當一個子組件接收一個函數作為 props,為了保證函數的引用不發生變化,有的人選擇使用 useCallback 來緩存函數引用,從而期望子組件不會因為 props 發生了變化而導致子組件重新渲染。MMR28資訊網——每日最新資訊28at.com

function Demo() {  ...  const change = useCallback(() => {}, [])  return (    <div>      ...      <Filter change={change} />    </div>  )}

結合我們剛才說的,這里只使用 useCallback 是做了無用功。MMR28資訊網——每日最新資訊28at.com

preProps = { change: change }curProps = { change: change }preProps === curProps // false

那么問題就來了,如果這樣子的話,豈不是每個組件的 props 都會發生變化了?MMR28資訊網——每日最新資訊28at.com

當然不是,React 內部針對 props 有另外一個策略:MMR28資訊網——每日最新資訊28at.com

如果父組件被判定為沒有變化,那么,在判斷子組件是否發生變化時,不會比較子組件的 props。MMR28資訊網——每日最新資訊28at.com

源碼里少一個判斷,卻衍生出這樣一個精妙的設計。MMR28資訊網——每日最新資訊28at.com

高級!MMR28資訊網——每日最新資訊28at.com

除此之外,Fiber Tree 的根節點,被判定為始終不會發生變化。MMR28資訊網——每日最新資訊28at.com

這樣,根節點的子組件在比較時,react 就一定會跳過 props 的比較,以此類推。我們就有機會構造一個高性能的更新過程。MMR28資訊網——每日最新資訊28at.com

回到我們經典的數字遞增案例,來分析這個案例。MMR28資訊網——每日最新資訊28at.com

function Child() {  console.log('我不想重新渲染')    return (    <div>我不想重新渲染</div>  )}export default function Demo02() {  const [count, setCount] = useState(0)  return (    <div className="wrapper">      <div onClick={() => setCount(count + 1)}>{count}</div>      <Child />    </div>  )}

當我們點擊數字的時候,數字遞增,父組件 Demo02 被判定為改變,因此,內部的所有子組件都需要比較 props,props 為不可變數據,子組件 Child 的 props 進行了如下比較,結果為 false 。MMR28資訊網——每日最新資訊28at.com

{} === {} // false

因此,Child 雖然不想 re-render,但是每次 count 變化都 render 了。MMR28資訊網——每日最新資訊28at.com

調整的方式非常簡單,只需要讓父組件的 state 沒有發生變化即可,把變化的部分單獨封裝在另外一個子組件里。MMR28資訊網——每日最新資訊28at.com

function Change() {  const [count, setCount] = useState(0)  return (    <div onClick={() => setCount(count + 1)}>{count}</div>  )}export default function Demo02() {  return (    <div className="wrapper">      <Change />      <Child />    </div>  )}

這個時候,父組件被判定為沒有發生變化,因此子組件就會跳過 props 的比較,從而 Child 判定為沒有發生變化。這樣我們的目的就達到了。MMR28資訊網——每日最新資訊28at.com

但是,這里有一個前期條件,那就是我們需要確保 Demo02 的父組件也被判定為沒有發生變化,因此,如果你是 React 架構師,頂層結構的設計是你需要關注的重中之重,因為如果頂層出了問題,導致父組件不滿足這樣的穩定結構,那么后續的子組件都會 re-render。MMR28資訊網——每日最新資訊28at.com

那么理解這個規則很難嗎?其實不難,難就難在,在看這篇文章之前,可能你壓根就不知道這個設計啊。MMR28資訊網——每日最新資訊28at.com

如果我們有一個不靠譜的 React 架構師,頂層組件的穩定結構出了問題,那么我們有什么手段,能夠低成本的讓你能接觸到的頁面結構保持穩定呢?MMR28資訊網——每日最新資訊28at.com

答案就是 React.memo。MMR28資訊網——每日最新資訊28at.com

memo 函數會讓組件的 props 比較方式發生變化,我們之前都是一直用的 === 全等比較,使用 memo 包裹組件之后,React 內部會改變比較策略,他會遍歷 props 的每個屬性,如果每個屬性都能通過全等比較,那么就判定為 props 沒有發生變化。MMR28資訊網——每日最新資訊28at.com

這個遍歷過程只會發生在 props 對象的第一層屬性,不會更進一步深入。MMR28資訊網——每日最新資訊28at.com

因此,當我們無法確定上層組件是否發生變化時,我們可以在某一個節點使用 memo 來確保從這一層開始建立穩定的高性能模式。MMR28資訊網——每日最新資訊28at.com

function _Child() {  console.log('我不想重新渲染')    return (    <div>我不想重新渲染</div>  )}var Child = memo(_Child)export default function Demo02() {  const [count, setCount] = useState(0)  return (    <div className="wrapper">      <div onClick={() => setCount(count + 1)}>{count}</div>      <Child />    </div>  )}

當我們使用 memo 包裹子組件導致 props 的比較方式發生變化時,useCallback 緩存引用就有用了。這也是 useCallback 的主要作用,他一定要結合 memo 去使用。MMR28資訊網——每日最新資訊28at.com

當然,我們也可以用一些騷操作來達到同樣的目標,利用 useMemo 來緩存組件。MMR28資訊網——每日最新資訊28at.com

export default function Demo02() {  const [count, setCount] = useState(0)  const _child = useMemo(() => {    return <Child />  }, [])  return (    <div className="wrapper">      <div onClick={() => setCount(count + 1)}>{count}</div>      {_child}    </div>  )}

當你決定要自己設計比較規則時就可以采用這樣的方式。MMR28資訊網——每日最新資訊28at.com

三、總結

這篇文章分享了兩個 React 項目性能優化的最重要的手段。我們只要了解了真實的底層機制,就能寫出高性能的代碼,他們的理解難度并不高。我們只需要在項目中正確的去編寫符合他們機制的代碼即可。MMR28資訊網——每日最新資訊28at.com

如果你是 React 項目架構師,那么你一定要吃透這個機制,在頂層架構中,我們會額外添加 Router/Redux 等諸多頂層組件,他們會不會導致高性能結構的崩塌,你一定要非常明確。MMR28資訊網——每日最新資訊28at.com

除此之外,當頂層的父組件不變判定被破壞,我們也不需要每一個組件都用 memo 包裹起來,只需要在合適的節點包裹一個組件即可。因為 memo 的比較本身也會增加程序的執行成本,大量的 memo 反而會導致性能變得更低。MMR28資訊網——每日最新資訊28at.com

除此之外,我們要明確,組件的 re-render 是內存行為,他是執行了一次 JS 函數,他并不會導致瀏覽器真的發生渲染行為,因此 re-render 的執行也是非常快速的,大多數情況下的 re-render 都可以接受,不過超大量的 re-render 會導致執行壓力變大,所以用大量 memo 減少 re-render 并不一定是一件劃算的事情。MMR28資訊網——每日最新資訊28at.com

利用少量的 memo 與 React 本身的緩存機制減少大量的 re-render 才是合理的方案。MMR28資訊網——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-62353-0.html理解這個機制,是成為React性能優化高手的關鍵

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: Spring為什么建議構造器注入?看看和你所理解的一樣嗎?

下一篇: 部署過Vue項目嗎?遇到404如何解決?

標簽:
  • 熱門焦點
  • K60 Pro官方停產 第三方瞬間漲價

    雖然沒有官方宣布,但Redmi的一些高管也已經透露了,Redmi K60 Pro已經停產且不會補貨,這一切都是為了即將到來的K60 Ultra鋪路,屬于廠家的正常操作。但有意思的是該機在停產之后
  • K60至尊版剛預熱 一加Ace2 Pro正面硬剛

    Redmi這邊剛如火如荼的宣傳了K60 Ultra的各種技術和硬件配置,作為競品的一加也坐不住了。一加中國區總裁李杰發布了兩條微博,表示在自家的一加Ace2上早就已經采用了和PixelWo
  • 7月安卓手機性價比榜:努比亞+紅魔兩款新機入榜

    7月登場的新機有努比亞Z50S Pro和紅魔8S Pro,除了三星之外目前唯二的兩款搭載超頻版驍龍8Gen2處理器的產品,而且努比亞和紅魔也一貫有著不錯的性價比,所以在本次的性價比榜單
  • Automa-通過連接塊來自動化你的瀏覽器

    1、前言通過瀏覽器插件可實現自動化腳本的錄制與編寫,具有代表性的工具就是:Selenium IDE、Katalon Recorder,對于簡單的業務來說可快速實現自動化的上手工作。Selenium IDEKat
  • 如何使用JavaScript創建一只圖像放大鏡?

    譯者 | 布加迪審校 | 重樓如果您曾經瀏覽過購物網站,可能遇到過圖像放大功能。它可以讓您放大圖像的特定區域,以便瀏覽。結合這個小小的重要功能可以大大改善您網站的用戶體驗
  • 三萬字盤點 Spring 九大核心基礎功能

    大家好,我是三友~~今天來跟大家聊一聊Spring的9大核心基礎功能。話不多說,先上目錄:圖片友情提示,本文過長,建議收藏,嘿嘿嘿!一、資源管理資源管理是Spring的一個核心的基礎功能,不
  • 一個注解實現接口冪等,這樣才優雅!

    場景碼猿慢病云管理系統中其實高并發的場景不是很多,沒有必要每個接口都去考慮并發高的場景,比如添加住院患者的這個接口,具體的業務代碼就不貼了,業務偽代碼如下:圖片上述代碼有
  • 華為發布HarmonyOS 4:更好玩、更流暢、更安全

    在8月4日的華為開發者大會2023(HDC.Together)大會上,HarmonyOS 4正式發布。自2019年發布以來,HarmonyOS一直以用戶為中心,經歷四年多的發展HarmonyOS已
  • 世界人工智能大會國際日開幕式活動在世博展覽館開啟

    30日上午,世界人工智能大會國際日開幕式活動在世博展覽館開啟,聚集國際城市代表、重量級院士專家、國際創新企業代表,共同打造人工智能交流平臺。上海市副市
Top 主站蜘蛛池模板: 红安县| 惠水县| 旬阳县| 额尔古纳市| 双流县| 丹凤县| 石林| 安宁市| 民县| 克什克腾旗| 广州市| 湟中县| 平度市| 嫩江县| 深泽县| 凌云县| 延寿县| 霞浦县| 南木林县| 中卫市| 方山县| 宣化县| 双峰县| 黄石市| 新蔡县| 四川省| 宜昌市| 莲花县| 武功县| 当阳市| 莱州市| 万安县| 静乐县| 临安市| 云浮市| 上饶县| 云霄县| 开封市| 任丘市| 连州市| 汕头市|