<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 代碼:規(guī)則、模式、注意事項

          共 4872字,需瀏覽 10分鐘

           ·

          2022-02-28 12:46

          本文將通過逐步實現(xiàn)一個簡單的應(yīng)用來帶大家看看如何編寫編寫高性能的 React 代碼。首先會以常規(guī)的模式來實現(xiàn)組件,然后再考慮性能的情況下重構(gòu)每個步驟,并從每個步驟中提取一個通用規(guī)則,這些規(guī)則可以應(yīng)用于大多數(shù)應(yīng)用程序。然后比較最后的結(jié)果。

          下面將編寫一個“國家設(shè)置”頁面,用戶可以從列表中選擇國家,查看該國家的信息,可以保存國家:

          可以看到,左側(cè)有一個國家列表,帶有“已保存”和“已選擇”狀態(tài),當(dāng)點擊列表中的選項時,右側(cè)會顯示該國家的詳細信息。當(dāng)按下保存按鈕時,選定的國家會變成已保存,已保存的選項的背景會變成藍色。

          1. 構(gòu)建應(yīng)用程序

          首先,根據(jù)設(shè)計圖,我們要考慮應(yīng)用的結(jié)構(gòu)以及要實現(xiàn)哪些組件:

          • 頁面組件:在其中處理提交邏輯和國家選擇邏輯;
          • 國家列表組件:將呈現(xiàn)列表中的所有國家,并進行過濾和排序等操作;
          • 國家選項組件:將所有國家呈現(xiàn)在國家列表組件中;
          • 選定國家組件:將呈現(xiàn)選定國家的詳細信息,并具有保存按鈕。

          當(dāng)然,這不是實現(xiàn)這個頁面的唯一方式,實現(xiàn)方式僅供參考。下面就來看看如何實現(xiàn)這些組件。

          2. 實現(xiàn)頁面組件

          下面終于要開始寫代碼了,下面就從根開始,實現(xiàn)Page組件,步驟如下:

          1. 需要組件包含頁面標(biāo)題、國家列表和選定國家組件;
          2. 將頁面參數(shù)中的國家列表數(shù)據(jù)傳遞給CountriesList組件,以便它可以呈現(xiàn)這些數(shù)據(jù);
          3. 頁面中應(yīng)該有一個已選中國家的狀態(tài),它將從 CountriesList 件接收,并傳遞給 SelectedCountry 組件;
          4. 頁面中應(yīng)該有一個已保存國家的狀態(tài),它將從 SelectedCountry 組件接收,并傳遞給 CountriesList 組件。
          export?const?Page?=?({?countries?}:?{?countries:?Country[]?})?=>?{
          ??const?[selectedCountry,?setSelectedCountry]?=?useState(countries[0]);
          ??const?[savedCountry,?setSavedCountry]?=?useState(countries[0]);

          ??return?(
          ????<>
          ??????

          Country?settings</h1>
          ??????
          ??????????????????countries={countries}
          ??????????onCountryChanged={(c)?=>?setSelectedCountry(c)}
          ??????????savedCountry={savedCountry}
          ????????/
          >
          ??????????????????country={selectedCountry}
          ??????????onCountrySaved={()?=>?setSavedCountry(selectedCountry)}
          ????????/>
          ??????</div>
          ????>
          ??);
          };

          3. 重構(gòu)頁面組件(考慮性能)

          在 React 中,當(dāng)組件的 state 或者 props 發(fā)生變化時,組件會重新渲染。在 Page 組件中,當(dāng) setSelectedCountry 或者 setSavedCountry 被調(diào)用時,組件就會重新渲染。當(dāng)組件的國家列表數(shù)據(jù)(props)發(fā)生變化時,組件也會重新渲染。CountriesList 和 SelectedCountry 組件也是如此,當(dāng)它們的 props 發(fā)生變化時,都會重新渲染。

          我們知道,React 會對 props 進行嚴(yán)格相等的比較,并且內(nèi)聯(lián)函數(shù)每次都會創(chuàng)建新值。這就導(dǎo)致了一個錯誤的的觀念:為了減少 CountriesList 和SelectedCountry 組件的重新渲染,需要通過在 useCallback 中包裝內(nèi)聯(lián)函數(shù)來避免在每次渲染中重新創(chuàng)建內(nèi)聯(lián)函數(shù)。

          export?const?Page?=?({?countries?}:?{?countries:?Country[]?})?=>?{

          ??const?onCountryChanged?=?useCallback((c)?=>?setSelectedCountry(c),?[]);
          ??const?onCountrySaved?=?useCallback(()?=>?setSavedCountry(selectedCountry),?[]);

          ??return?(
          ????<>
          ??????...
          ??????????????????onCountryChanged={onCountryChange}
          ????????/>
          ??????????????????onCountrySaved={onCountrySaved}
          ????????/>
          ??????...
          ????</>
          ??);
          };

          而實際上,這樣并不會起作用。因為它沒有考慮到:如果父組件 Page 被重新渲染,子組件 CountriesList 也總是會重新渲染,即使它根本沒有任何 props。

          可以這樣來簡化 Page 組件:

          const?CountriesList?=?()?=>?{
          ??console.log("Re-render!!!!!");
          ??return?
          countries?list,?always?re-renders</div>;
          };

          export?const?Page?=?({?countries?}:?{?countries:?Country[]?})?=>?{
          ??const?[counter,?setCounter]?=?useState(1);

          ??return?(
          ????<>
          ??????

          Country?settingsh1>
          ??????()?=>?setCounter(counter?+?1)}>
          ????????Click?here?to?re-render?Countries?list?(open?the?console)?{counter}
          ??????</button>
          ??????>
          ????</>
          ??);
          };

          當(dāng)每次點擊按鈕時,即使沒有任何 props,都會看到 CountriesList 組件被重新渲染。由此,總結(jié)出第一條規(guī)則:「如果想把 props 中的內(nèi)聯(lián)函數(shù)提取到 useCallback 中,以此來避免子組件的重新渲染,請不要這樣做,它不起作用。」

          現(xiàn)在,有幾種方法可以處理上述情況,最簡單的一種就是使用 useMemo,它本質(zhì)上就是緩存?zhèn)鬟f給它的函數(shù)的結(jié)果。并且僅在 useMemo 的依賴項發(fā)生變化時才會重新執(zhí)行。這就就將 CountriesList 組件使用 useMemo 包裹,只有當(dāng) useMemo 依賴項發(fā)生變化時,才會重新渲染 ComponentList 組件:

          export?const?Page?=?({?countries?}:?{?countries:?Country[]?})?=>?{
          ??const?[counter,?setCounter]?=?useState<number>(1);

          ??const?list?=?useMemo(()?=>?{
          ????return?;
          ??},?[]);

          ??return?(
          ????<>
          ??????

          Country?settings</h1>
          ???????setCounter(counter?+?1)}>
          ????????Click?here?to?re-render?Countries?list?(open?the?console)?{counter}
          ??????button>
          ??????{list}
          ????</>
          ??);
          };

          當(dāng)然,在這個簡化的例子中是不行的,因為它沒有任何依賴項。那我們該如何簡化 Page 頁面呢?下面再來看一下它的結(jié)構(gòu):

          export?const?Page?=?({?countries?}:?{?countries:?Country[]?})?=>?{
          ??const?[selectedCountry,?setSelectedCountry]?=?useState(countries[0]);
          ??const?[savedCountry,?setSavedCountry]?=?useState(countries[0]);

          ??return?(
          ????<>
          ??????

          Country?settings</h1>
          ??????
          ??????????????????countries={countries}
          ??????????onCountryChanged={(c)?=>?setSelectedCountry(c)}
          ??????????savedCountry={savedCountry}
          ????????/
          >
          ??????????????????country={selectedCountry}
          ??????????onCountrySaved={()?=>?setSavedCountry(selectedCountry)}
          ????????/>
          ??????</div>
          ????>
          ??);
          };

          可以看到:

          • 在 CountriesList 組件中不會使到 selectedCountry 狀態(tài);
          • 在 SelectedCountry 組件中不會使用到 savedCountry 狀態(tài);

          這意味著當(dāng) selectedCountry 狀態(tài)發(fā)生變化時,CountriesList 組件不需要重新渲染。savedCountry 狀態(tài)發(fā)生變化時,SelectedCountry 組件也不需要重新渲染。可以使用 useMemo 來包裹它們,以防止不必要的重新渲染:

          export?const?Page?=?({?countries?}:?{?countries:?Country[]?})?=>?{
          ??const?[selectedCountry,?setSelectedCountry]?=?useState(countries[0]);
          ??const?[savedCountry,?setSavedCountry]?=?useState(countries[0]);

          ??const?list?=?useMemo(()?=>?{
          ????return?(
          ??????????????countries={countries}
          ????????onCountryChanged={(c)?=>?setSelectedCountry(c)}
          ????????savedCountry={savedCountry}
          ??????/>
          ????);
          ??},?[savedCountry,?countries]);

          ??const?selected?=?useMemo(()?=>?{
          ????return?(
          ??????????????country={selectedCountry}
          ????????onCountrySaved={()?=>?setSavedCountry(selectedCountry)}
          ??????/>
          ????);
          ??},?[selectedCountry]);

          ??return?(
          ????<>
          ??????

          Country?settings</h1>
          ??????
          ????????{list}
          ????????{selected}
          ??????div>
          ????</>
          ??);
          };

          由此總結(jié)出第二條規(guī)則:「如果組件需要管理狀態(tài),就找出渲染樹中不依賴于已更改狀態(tài)的部分,并將其使用 useMemo 包裹,以減少其不必要的重新渲染?!?/strong>

          4. 實現(xiàn)國家列表組件

          Page 頁面已經(jīng)完美實現(xiàn)了,是時候編寫它的子組件了。首先來實現(xiàn)比較復(fù)雜的 CountriesList 組件。這個組件一個接收國家列表數(shù)據(jù),當(dāng)在列表中選中一個國家時,會觸發(fā) onCountryChanged 回調(diào),并應(yīng)以不同的顏色突出顯示保存的國家:

          type?CountriesListProps?=?{
          ??countries:?Country[];
          ??onCountryChanged:?(country:?Country)?=>?void;
          ??savedCountry:?Country;
          };

          export?const?CountriesList?=?({
          ??countries,
          ??onCountryChanged,
          ??savedCountry
          }:?CountriesListProps)?=>?{
          ??const?Item?=?({?country?}:?{?country:?Country?})?=>?{
          ????//?根據(jù)國家選項是否已選中來切換不同的className
          ????const?className?=?savedCountry.id?===?country.id???"country-item?saved"?:?"country-item";

          ????const?onItemClick?=?()?=>?onCountryChanged(country);
          ????return?(
          ??????
          ????????



          瀏覽 47
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <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>
                    欧美日韩在线看 | AV第一福利大全导航 | 操逼电影中文字幕 | 骚货舔鸡吧 | 中国婬乱a—级毛片多女 |