<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          奇怪的useMemo知識(shí)增加了

          共 2036字,需瀏覽 5分鐘

           ·

          2021-02-19 10:56

          作為「性能優(yōu)化」手段,一般用useMemo緩存函數(shù)組件中比較消耗性能的計(jì)算結(jié)果:

          function?App()?{
          ??const?memoizedValue?=?useMemo(
          ????()?=>?computeExpensiveValue(a,?b),
          ????[a,?b]
          ??);
          ??//?...
          }

          只有在依賴項(xiàng)改變后才會(huì)重新計(jì)算新的memoizedValue。

          你有沒有想過,如果用useMemo緩存函數(shù)組件的返回值,會(huì)怎么樣呢?

          舉個(gè)例子

          我們有個(gè)全局context —— AppContext。

          由于同學(xué)們偷懶,隨著項(xiàng)目的迭代,新增的context都選擇放在AppContext里,導(dǎo)致AppContext包含的內(nèi)容越來越多。

          現(xiàn)在我們有個(gè)Tree組件,他會(huì)渲染一個(gè)很耗性能的大組件ExpensiveTree。

          function?Tree()?{
          ??let?appContextValue?=?useContext(AppContext);
          ??let?theme?=?appContextValue.theme;

          ??return?<ExpensiveTree?className={theme}?/>;
          }

          該組件內(nèi)部依賴AppContext中的theme狀態(tài)。

          由于AppContext中包含很多與theme無關(guān)的state,導(dǎo)致每次其他無關(guān)的state更新,Tree都會(huì)重新render,進(jìn)而ExpensiveTree組件也重新render。

          現(xiàn)在這個(gè)優(yōu)化任務(wù)交到了你手上,該怎么辦呢?

          優(yōu)化ExpensiveTree

          這時(shí)候,useMemo就能派上用場(chǎng):

          function?Tree()?{
          ??let?appContextValue?=?useContext(AppContext);
          ??let?theme?=?appContextValue.theme;

          ??return?useMemo(()?=>?{
          ????return?<ExpensiveTree?className={theme}?/>;
          ??},?[theme])
          }

          我們將返回的ExpensiveTree作為useMemo返回值,theme作為依賴。

          這樣,即使AppContext改變導(dǎo)致Tree反復(fù)render,ExpensiveTree也只會(huì)在theme改變后render

          原理解析

          要理解這么做有效的原因,需要了解三點(diǎn):

          1. useMemo返回值是什么

          2. 函數(shù)組件的返回值是什么

          3. React組件在什么時(shí)候render

          回答第一個(gè)問題:useMemo會(huì)將第一個(gè)參數(shù)(函數(shù))的返回值保存在組件對(duì)應(yīng)fiber中,只有在依賴項(xiàng)(第二個(gè)參數(shù))變化后才會(huì)重新調(diào)用第一個(gè)參數(shù)(函數(shù))計(jì)算一個(gè)新值。

          回答第二個(gè)問題:函數(shù)組件的返回值是JSX對(duì)象。

          同一個(gè)函數(shù)組件調(diào)用多次,返回的是多個(gè)「不同」JSX對(duì)象(即使props未變,但JSX是新的引用)。

          按照以上兩個(gè)回答,我們可以得出結(jié)論:

          以上useMemo用法實(shí)際上在函數(shù)組件對(duì)應(yīng)的fiber中緩存了一個(gè)完整的JSX對(duì)象

          第三個(gè)問題,函數(shù)組件需要同時(shí)滿足如下條件才不會(huì)render

          1. oldProps === newProps

          前后兩次更新props全等,注意是「全等」

          1. 組件context沒有變化

          2. workInProgress.type === current.type

          組件更新前后fiber.type未變化,比如div沒有變?yōu)?code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(145, 109, 213);font-weight: bolder;background-image: none;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;">p。

          1. !includesSomeLane(renderLanes, updateLanes)

          當(dāng)前fiber上不存在更新,或者存在更新但優(yōu)先級(jí)低。

          更詳細(xì)的解釋,可以參考這篇文章:React組件到底什么時(shí)候render?

          當(dāng)我們不使用useMemo包裹返回值,每次Tree render返回的都是全新的JSX對(duì)象。

          所以對(duì)于ExpensiveTree,oldProps !== newProps

          再看2:ExpensiveTree內(nèi)部context沒變,滿足

          再看3:ExpensiveTree更新前后type都是ExpensiveTree,滿足

          再看4: ExpensiveTree內(nèi)沒有狀態(tài)更新,滿足

          所以,當(dāng)我們使用useMemo包裹ExpensiveTree后,當(dāng)theme不變,每次Tree render后返回的都是同一個(gè)JSX對(duì)象,滿足第一條。

          基于這個(gè)原因,ExpensiveTree不會(huì)render。

          總結(jié)

          這篇文章提到的useMemo用法,并未在官網(wǎng)文檔中體現(xiàn),而是在#15156[1]中由Dan介紹。

          相比Vue,React更靈活,開發(fā)過程中需要開發(fā)者注意更多細(xì)節(jié)。要完全了解React,可能需要學(xué)習(xí)一些源碼層面的知識(shí)。

          參考資料

          [1]

          #15156: https://github.com/facebook/react/issues/15156#issuecomment-474590693


          瀏覽 32
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日本一级婬片A片AAA毛片男男 | 日本特色特黄A级片 | EvE依然私人订制 | 亚洲无砖无码 | 奶大灬舒服灬一进一出三区 |