<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>

          【React作者】:在你寫memo()之前需要注意的!

          共 5886字,需瀏覽 12分鐘

           ·

          2021-03-24 12:16

          原文地址:overreacted.io/zh-hans/before-you-memo

          作者:Dan Abramov@reactjs

          關(guān)注公眾號 前端人,回復(fù)“加群

          添加無廣告優(yōu)質(zhì)學(xué)習(xí)群


          有很多描寫React性能優(yōu)化的文章。一般而言,如果某些state更新緩慢的話,你需要:

          1. 驗(yàn)證是否正在運(yùn)行一個生產(chǎn)環(huán)境的構(gòu)建。(開發(fā)環(huán)境構(gòu)建會刻意地緩慢一些,極端情況下可能會慢一個數(shù)量級)
          2. 驗(yàn)證是否將樹中的狀態(tài)放在了一個比實(shí)際所需更高的位置上。(例如,將輸入框的state放到了集中的store里可能不是一個好主意)
          3. 運(yùn)行React開發(fā)者工具來檢測是什么導(dǎo)致了二次渲染,以及在高開銷的子樹上包裹memo()。(以及在需要的地方使用useMemo()

          最后一步是很煩人的,特別是對于介于兩者之間的組件,理想情況下,編譯器可以為您完成這一步。未來也許會。

          在這篇文章里,我想分享兩種不同的技巧。它們十分基礎(chǔ),這也正是為什么人們很少會意識到它們可以提升渲染性能。

          這些技巧和你已經(jīng)知道的內(nèi)容是互補(bǔ)的 它們并不會替代memo 或者 useMemo,但是先試一試它們還是不錯的

          一個(人工)減緩的組件 這里是一個具有嚴(yán)重渲染性能問題的組件

          import { useState } from 'react';
          export default function App({
            let [color, setColor] = useState('red');
            return (
              <div>
                <input 
                  value={color} 
                  onChange={(e) =>
           {
                    setColor(e.target.value)
                 }} />
                <p style={{ color }}>Hello, world!</p>
                <ExpensiveTree />
              </div>

            );
          }

          function ExpensiveTree({
            let now = performance.now();
            while (performance.now() - now < 100) {
              // Artificial delay -- do nothing for 100ms
            }
            return <p>I am a very slow component tree.</p>;
          }

          問題就是當(dāng)App中的color變化時,我們會重新渲染一次被我們手動大幅延緩渲染的<ExpensiveTree />組件。

          我可以直接在它上面寫個memo()然后收工大吉,但是現(xiàn)在已經(jīng)有很多這方面的文章了,所以我不會再花時間講解如何使用memo()來優(yōu)化。

          我只想展示 兩種不同的解決方案。

          解法 1:向下移動State

          如果你仔細(xì)看一下渲染代碼,你會注意到返回的樹中只有一部分真正關(guān)心當(dāng)前的color:

          export default function App({
            let [color, setColor] = useState('red');
            return (
              <div>
                <input value={color} onChange={(e) => setColor(e.target.value)} />
                <p style={{ color }}>Hello, world!</p>
                <ExpensiveTree />
              </div>

            );
          }


          所以讓我們把這一部分提取到Form組件中然后將state移動到該組件里:

          export default function App({
            return (
              <>
                <Form />
                <ExpensiveTree />
              </>

            );
          }

          function Form({
            let [color, setColor] = useState('red');
            return (
              <>
                <input value={color} onChange={(e) => setColor(e.target.value)} />
                <p style={{ color }}>Hello, world!</p>
              </>

            );
          }

          現(xiàn)在如果color變化了,只有Form會重新渲染。問題解決了。

          解法 2:內(nèi)容提升

          當(dāng)一部分state在高開銷樹的上層代碼中使用時上述解法就無法奏效了。舉個例子,如果我們將color放到父元素div中。

          export default function App({
            let [color, setColor] = useState('red');
            return (
              <div style={{ color }}>
                <input value={color} onChange={(e) => setColor(e.target.value)} />
                <p>Hello, world!</p>
                <ExpensiveTree />
              </div>

            );
          }

          現(xiàn)在看起來我們似乎沒辦法再將不使用color的部分提取到另一個組件中了,因?yàn)檫@部分代碼會首先包含父組件的div,然后才包含 `` `。這時候無法避免使用memo了,對嗎?又或者,我們也有辦法避免?

          答案顯而易見:

          export default function App({
            return (
              <ColorPicker>
                <p>Hello, world!</p>
                <ExpensiveTree />
              </ColorPicker>

            );
          }

          function ColorPicker({ children }{
            let [color, setColor] = useState("red");
            return (
              <div style={{ color }}>
                <input value={color} onChange={(e) => setColor(e.target.value)} />
                {children}
              </div>

            );
          }

          我們將App組件分割為兩個子組件。依賴color的代碼就和color state變量一起放入ColorPicker組件里。

          不關(guān)心color的部分就依然放在App組件中,然后以JSX內(nèi)容的形式傳遞給ColorPicker,也被稱為children屬性。當(dāng)color變化時,ColorPicker會重新渲染。但是它仍然保存著上一次從App中拿到的相同的children屬性,所以React并不會訪問那棵子樹。因此,ExpensiveTree不會重新渲染。

          原因是什么?在你用memo或者useMemo做優(yōu)化時,如果你可以從不變的部分里分割出變化的部分,那么這看起來可能是有意義的。

          關(guān)于這些方式有趣的部分是他們本身并不真的和性能有關(guān). 使用children屬性來拆分組件通常會使應(yīng)用程序的數(shù)據(jù)流更容易追蹤,并且可以減少貫穿樹的props數(shù)量。在這種情況下提高性能是錦上添花,而不是最終目標(biāo)。

          奇怪的是,這種模式在將來還會帶來更多的性能好處。舉個例子,當(dāng)服務(wù)器組件 穩(wěn)定且可被采用時,我們的ColorPicker組件就可以從服務(wù)器上獲取到它的children。

          整個<ExpensiveTree />組件或其部分都可以在服務(wù)器上運(yùn)行,即使是頂級的React狀態(tài)更新也會在客戶機(jī)上“跳過”這些部分。這是memo做不到的事情!但是,這兩種方法是互補(bǔ)的。不要忽視state下移(和內(nèi)容提升!) 然后,如果這還不夠,那就使用Profiler然后用memo來寫吧。

          最后

          1. 公眾號里回復(fù)關(guān)鍵詞資料包領(lǐng)取我整理的進(jìn)階資料包
          2. 公眾號里回復(fù)關(guān)鍵詞加群,加入前端進(jìn)階群
          3. 文章點(diǎn)個在看,支持一下把!

          點(diǎn)擊關(guān)注我們↓

          瀏覽 48
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <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片 | 九九视频黄片 |