最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

React性能優(yōu)化的實現(xiàn)方法詳解

 更新時間:2023年01月30日 08:30:49   作者:我是真的cai  
react憑借virtual DOM和diff算法擁有高效的性能,除此之外也有很多其他的方法和技巧可以進(jìn)一步提升react性能,在本文中我將列舉出可有效提升react性能的幾種方法,幫助我們改進(jìn)react代碼,提升性能

前言

想要寫出高質(zhì)量的代碼,僅僅靠框架底層幫我們的優(yōu)化還遠(yuǎn)遠(yuǎn)不夠,在編寫的過程中,需要我們自己去使用提高的 api,或者根據(jù)它底層的原理去做一些優(yōu)化,以及規(guī)范。

相比于 Vue ,React 不會再框架源碼層面幫助我們直接解決一下基本的性能優(yōu)化相關(guān),而是提供一下 API (Hooks)讓我們自己去優(yōu)化我們的應(yīng)用,也是它自身更靈活的一種原因之一。

下面總結(jié)了一些從編寫 React 代碼層面上能做的優(yōu)化點(diǎn)。

遍歷視圖key使用

key 的作用能夠幫助我們識別哪些元素改變了,比如添加和刪除。在 React 更新時,會觸發(fā) React Diff 算法,diff 過程中過借助 key 值來判斷元素是新創(chuàng)建還是需要移動的元素。React 會保存這個輔助狀態(tài)。從而減少不必要的元素渲染。

key 的值最好是當(dāng)前列表中擁有獨(dú)一無二的字符串。開發(fā)中通常用 id 等這些作為元素的 key 值。

當(dāng)前的列表不會發(fā)生操作,萬不得已 可以使用 index 作為 key 值。

key 應(yīng)該具有穩(wěn)定,可預(yù)測,以及列表內(nèi)唯一的特質(zhì)。不穩(wěn)定的 key 比如 Math.random() 生成的會 導(dǎo)致很多組件實例和 DOM 節(jié)點(diǎn)被不必要的重新創(chuàng)建,這可能導(dǎo)致性能下降和子組件狀態(tài)丟失等等。

React.memo緩存組件

react 是單向數(shù)據(jù)流,父組件狀態(tài)的更新也會讓子組件一起重新渲染更新,即使子組件的狀態(tài)沒有發(fā)生變化,不會像 Vue 一樣能夠具體監(jiān)聽到某一個組件狀態(tài)的變化然后更新當(dāng)前的這個組件。

因此可以用 React.memo 緩存組件,這樣只有傳入當(dāng)前組件狀態(tài)值變化時才會重新渲染,值相同那么就會緩存組件。

// 子組件
const Child = React.memo(() => {
  console.log("child");
  return (
    <div>
      Child
    </div>
  );
});
// 父組件
function App() {
  const [count, setCount] = useState(0);
  return (
    <div className="App">
      <h3>{count}</h3>
      <button onClick={() => setCount(count + 1)}>Count++ </button>
      <Child />
    </div>
  );
}

上面代碼 <Child /> 組件添加上 memo 每次點(diǎn)擊 count ++ 那么就會不會重新渲染了。

React.useCallback讓函數(shù)保持相同的引用

像上面的例子,如果父組件想拿到子組件的狀態(tài)值,通常會使用 callback 的方式傳遞出去給父組件。

interface ChildProps {
  onChangeNum: (value: number) => void;
}
const Child: React.FC<ChildProps> = React.memo(({ onChangeNum }) => {
  console.log("child");
  const [num, setNum] = useState(0);
  useEffect(() => {
    onChangeNum(num);
  }, [num]);
  return (
    <div>
      <button
        onClick={() => {
          setNum((prevState) => {
            return prevState + 1;
          });
        }}
      >
        Child
      </button>
    </div>
  );
});
function App() {
  const [count, setCount] = useState(0);
  return (
    <div className="App">
      <h3>{count}</h3>
      <button onClick={() => setCount(count + 1)}>Count++ </button>
      <Child
        onChangeNum={(num) => {
          console.log(num, "childNum");
        }}
      />
    </div>
  );
}

組件每次更新 num 值,父組件通過 onChangeNum 回掉函數(shù)方式接受。

注意剛才說的 memo 能夠在組件傳入值不變的情況下緩存組件避免重新渲染,但是,這里又失效了。這是為什么呢?

原因就是父組件更新了,每次都會創(chuàng)建一個新的 onChangeNum ,相當(dāng)于屬于不同的引用了,在每次 props 傳遞的回掉函數(shù)都不相同,所以 memo 失去了作用。

那么該怎么解決?那就是使用 useCallback hook 幫助我們保持相同的引用。

<Child
  onChangeNum={useCallback((num) => {
    console.log(num, "childNum");
  }, [])}
/>

開發(fā)中使用了 memo 緩存了組件,還需要注意是否有匿名函數(shù)傳遞給子組件。

并不一定只在這種情況下才使用 useCallback ,比如一個請求函數(shù)或者邏輯處理函數(shù),也可以用 useCallback 包裹,不過要注意,內(nèi)部引用了外部的狀態(tài)或者值的相關(guān)聯(lián),那么需要在第二個參數(shù)也就是依賴數(shù)組里面添加上用到的某些值。

避免使用內(nèi)聯(lián)對象

在使用內(nèi)聯(lián)對象,react 每次重新渲染時會重新創(chuàng)建此對象,在更新組件對比 props ,oldProps === newProps 只要為 false 那么就會 re-render 。

如果TestComponent 組件重新渲染,那么就會新建創(chuàng)建 someProps 引用。傳遞給 RootComponent 組件每次判斷新舊 props 結(jié)果不同,導(dǎo)致也重新渲染。

const TestComponent = () => {
  const someProps = { value: '1' }
  return <RootComponent someProps={someProps} />;
};

更好的方式是,使用 ES6 擴(kuò)展運(yùn)算符的將這個對象展開,引用類型變?yōu)橹殿愋蛡鬟f,這樣再對比 props 就會相等了。

const TestComponent = () => {
  const someProps = { value: '1' }
  return <RootComponent {...someProps} />;
};

使用React.useMemo緩存計算結(jié)果或者組件

如 React 文檔所說,useMemo 的基本作用是,避免每次渲染都進(jìn)行高開銷的計算。

如果是一個功能組件里面,涉及到大型的計算,組件每次重新渲染導(dǎo)致都從新調(diào)用大型的計算函數(shù),這是非常消耗性能的,我們可以使用 useMemo 來緩存這個函數(shù)的計算結(jié)果,來減少 JavaScript 在呈現(xiàn)組件期間必須執(zhí)行的工作量,來縮短阻塞主線程的時間。

// 只有當(dāng) id 發(fā)生變化的時候才會從新計算
const TestComponent = () => {
  const value = useMemo(() => {
    return expensiveCalculation()
  }, [id]) 
  return <Component countValue={value} />
}

在使用 useMemo 緩存計算結(jié)果之前,還需要在適當(dāng)?shù)牡胤綉?yīng)用,useMemo 也是有成本的,它也會增加整體程序初始化的耗時,除非這個計算真的很昂貴,比如階乘計算。

所以并不適合全局使用,它更適合做局部的優(yōu)化。不應(yīng)該過度 useMemo。

另外在緩存結(jié)果值的同時,還可以用來緩存組件。

比如有一個全局 context ,隨著長期項目迭代 context 里面塞了很多狀態(tài),我們知道,context 的 value 發(fā)生變化,就會導(dǎo)致組件的重新渲染,而這個組件時一個很消耗性能的大型組件,只會被其中一個變量所影響才重新渲染,這時候就可以考慮使用 useMemo 進(jìn)行緩存。

const TestComponent = () => {
  const appContextValue = useContext(AppContext);
  const theme = appContextValue.theme;
  return useMemo(() => {
    return <RootComponent className={theme} />;
  }, [theme]);
};

<RootComponent /> 只有在 theme 變量發(fā)生變化的時候重新渲染。

使用React.Fragment片段

react 有規(guī)定組件中必須有一個父元素,但是在某些情況下,根標(biāo)簽不需要任何的屬性,這會導(dǎo)致整個應(yīng)用程序內(nèi)創(chuàng)建許多無用的元素,那么這個標(biāo)簽的作用并沒有太大的意義。

const TestComponent = () => {
  return (
    <div>
      <ChildA />
      <ChildB />
      <ChildC />
    </div>
  );
}

實際上頁面上的元素越多,DOM結(jié)構(gòu)嵌套越深,加載所需的時間就越多,也會增加瀏覽器的渲染壓力。

因此 React 提供了 Fragment 組件來代替包裹外層,它不會幫我們額外的創(chuàng)建外層 div 標(biāo)簽。

const TestComponent = () => {
  return (
    <React.Fragment>
      <ChildA />
      <ChildB />
      <ChildC />
    </React.Fragment>
  );
}

或者另一種簡潔的方式使用空標(biāo)簽 <></> 代替也是一樣的效果:

const TestComponent = () => {
  return (
    <>
      <ChildA />
      <ChildB />
      <ChildC />
    </>
  );
}

另外還有一些實用的場景,根據(jù)條件渲染元素

const TestComponent = () => {
  const { isLogin, name } = useApp();
  return (
    <>
      {isLogin ? (
        <>
          <h3>Welcome {name}</h3>
          <p>You are logged in!</p>
        </>
      ) : (
        <h3>go login...</h3>
      )}
    </>
  );
};

組件懶加載

應(yīng)用程序初始化加載的快慢也跟組件的數(shù)量有關(guān),因此在初始化的時候,一些我們看不見的頁面,也就是最開始用不到的組件可以選擇延遲加載組件,我們可以想到的是路由的懶加載,這樣來提升頁面的加載速度和響應(yīng)時間。

react 提供了 React.LazyReact.Suspense 來幫我們實現(xiàn)組件的懶加載。

import React, { lazy, Suspense } from 'react';
const AvatarComponent = lazy(() => import('./AvatarComponent'));
const renderLoader = () => <p>Loading</p>;
const DetailsComponent = () => (
  <Suspense fallback={renderLoader()}>
    <AvatarComponent />
  </Suspense>
)

Suspense 作用就是彌補(bǔ)在 Lazy 組件加載完成之前這段空白時間所能做的事情,尤其在組件較大,或者在較弱的設(shè)備和網(wǎng)絡(luò)中,就可以通過 fallback 屬性添加一個 loading 提示用戶正在加載的狀態(tài)。異步組件加載完成之后就會顯示出來。

如果單獨(dú)使用 lazy React 會在控制臺發(fā)出錯誤提示!

通過 CSS 加載和卸載組件

渲染是昂貴的,如果頻繁加載/卸載‘很重’的組件,這個操作可能非常消耗性能或者導(dǎo)致延遲。正常情況下,我們都會用三元運(yùn)算符在判斷加載顯示,也導(dǎo)致了一個問題,每次頻繁更新,觸發(fā)加載不同的組件,就會有一定的性能損耗。這時我們可以使用 CSS 屬性將其隱藏,讓 DOM 能夠保留在頁面當(dāng)重。

**不過這種方式并不是萬能的,可能會導(dǎo)致一些布局或者窗口發(fā)生錯位的問題。**但我們應(yīng)該選擇在不是這種情況下使用調(diào)整CSS的方法。另外一點(diǎn),將不透明度調(diào)整為0對瀏覽器的成本消耗幾乎為0(因為它不會導(dǎo)致重排),并且應(yīng)盡可能優(yōu)先于更該visibility 和 display。

// 避免對大型的組件頻繁對加載和卸載
const ViewExample = () => {
  const [isTest, setIsTest] = useState(true)
  return (
    <>
      { isTest ? <ViewComponent /> : <TestComponent />}
    </>
  );
};
// 使用該方式提升性能和速度
const visibleStyles = { opacity: 1 };
const hiddenStyles = { opacity: 0 };
const ViewExample = () => {
  const [isTest, setIsTest] = useState(true)
  return (
    <>
      <ViewComponent style={!isTest ? visibleStyles : hiddenStyles} /> 
			<TestComponent style={{ isTest ? visibleStyles : hiddenStyles }} />
    </>
  );
};

變與不變的地方做分離

通常使用 useMemo、useCallback 進(jìn)行優(yōu)化,這里說說不借助這些Hooks進(jìn)行優(yōu)化,

變與不變做分離的概念來源,其實就是因為自身的react 的機(jī)制,父組件的狀態(tài)更新了,所有的子組件得跟著一起渲染,意思是將有狀態(tài)的組件和無狀態(tài)的組件分離開。

function ExpensiveCpn() {
  console.log("ExpensiveCpn");
  let now = performance.now();
  while (performance.now() - now < 100) {}
  return <p>耗時的組件</p>;
}
export default function App() {
  const [num, updateNum] = useState("");
  return (
    <>
      <input
        type="text"
        onChange={(e) => updateNum(e.target.value)}
        value={num}
      />
      <ExpensiveCpn />
    </>
  );
}

上面輸入框輸入都會刷新組件<ExpensiveCpn/>,我們可以不使用 useMemo 等API就能控制渲染其實就是將變得和不變的分離開????:

function ExpensiveCpn() {
  console.log("ExpensiveCpn");
  let now = performance.now();
  while (performance.now() - now < 100) {}
  return <p>耗時的組件</p>;
}
function Input() {
  const [num, updateNum] = useState("");
  return (
    <input
      type="text"
      onChange={(e) => updateNum(e.target.value)}
      value={num}
    />
  );
}
export default function App() {
  return (
    <>
      <Input />
      <ExpensiveCpn />
    </>
  );
}

這樣渲染的組件只會是 <Input/>組件內(nèi)部,不會影響到外部。

總結(jié)

上面一些方式,可以從幾個方面理解:

  • 減少重新render的次數(shù):memo、useMemo、useCallback 使用、避免使用內(nèi)聯(lián)對象、變與不變的分離。
  • 減少渲染的節(jié)點(diǎn):React.Fragment 片段、組件懶加載。
  • 降低渲染計算量:遍歷試圖使用 key。

到此這篇關(guān)于React性能優(yōu)化的實現(xiàn)方法詳解的文章就介紹到這了,更多相關(guān)React性能優(yōu)化內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • React中mobx和redux的區(qū)別及說明

    React中mobx和redux的區(qū)別及說明

    這篇文章主要介紹了React中mobx和redux的區(qū)別及說明,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-06-06
  • react組件的創(chuàng)建與更新實現(xiàn)流程詳解

    react組件的創(chuàng)建與更新實現(xiàn)流程詳解

    React組件分為函數(shù)組件與class組件;函數(shù)組件是無狀態(tài)組件,class稱為類組件;函數(shù)組件只有props,沒有自己的私有數(shù)據(jù)和生命周期函數(shù);class組件有自己私有數(shù)據(jù)(this.state)和生命周期函數(shù)
    2022-10-10
  • 詳解超簡單的react服務(wù)器渲染(ssr)入坑指南

    詳解超簡單的react服務(wù)器渲染(ssr)入坑指南

    這篇文章主要介紹了詳解超簡單的react服務(wù)器渲染(ssr)入坑指南,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-02-02
  • React中Refs的使用場景及核心要點(diǎn)詳解

    React中Refs的使用場景及核心要點(diǎn)詳解

    在使用?React?進(jìn)行開發(fā)過程中,或多或少使用過?Refs?進(jìn)行?DOM?操作,這篇文章主要介紹了?Refs?功能和使用場景以及注意事項,希望對大家有所幫助
    2023-07-07
  • React項目中應(yīng)用TypeScript的實現(xiàn)

    React項目中應(yīng)用TypeScript的實現(xiàn)

    TypeScript通常都會依賴于框架,例如和vue、react 這些框架結(jié)合,本文就主要介紹了React項目中應(yīng)用TypeScript的實現(xiàn),分享給大家,具體如下:
    2021-09-09
  • 詳解React中多種組件通信方式的實現(xiàn)

    詳解React中多種組件通信方式的實現(xiàn)

    在React中,組件之間的通信是一個非常重要的話題,React提供了幾種方式來實現(xiàn)跨組件通信,下面小編將詳細(xì)講講其中幾種通信方式,并提供實際的代碼示例,需要的可以參考下
    2023-11-11
  • 用react實現(xiàn)一個簡單的scrollView組件

    用react實現(xiàn)一個簡單的scrollView組件

    這篇文章主要給大家介紹一下如何用 react 實現(xiàn)一個簡單的 scrollView組件,文中有詳細(xì)的代碼示例,具有一定的參考價值,需要的朋友可以參考下
    2023-07-07
  • React如何避免重渲染

    React如何避免重渲染

    這篇文章主要介紹了React如何避免重渲染,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-04-04
  • react組件封裝input框的防抖處理的項目實現(xiàn)

    react組件封裝input框的防抖處理的項目實現(xiàn)

    本文主要介紹了react組件封裝input框的防抖處理的項目實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • TypeScript在React中的應(yīng)用技術(shù)實例解析

    TypeScript在React中的應(yīng)用技術(shù)實例解析

    這篇文章主要為大家介紹了TypeScript在React中的應(yīng)用技術(shù)實例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-04-04

最新評論

涪陵区| 兴仁县| 张家口市| 建德市| 雅安市| 雅安市| 商河县| 青田县| 镇宁| 伊宁县| 绥中县| 阿勒泰市| 华阴市| 同江市| 呈贡县| 房产| 利川市| 获嘉县| 刚察县| 巫溪县| 三河市| 秭归县| 西青区| 辽宁省| 乐陵市| 会同县| 石嘴山市| 张掖市| 屯留县| 平陆县| 卢龙县| 灵台县| 叙永县| 始兴县| 高淳县| 宁武县| 青神县| 波密县| 崇仁县| 沈阳市| 固镇县|