中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何利用Memoization提高React性能

發布時間:2022-03-30 09:36:45 來源:億速云 閱讀:133 作者:iii 欄目:web開發

本篇內容介紹了“如何利用Memoization提高React性能”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

如何利用Memoization提高React性能

React 是如何渲染視圖的?

在討論 React 中的 Memoization 細節之前,讓我們先來看看 React 是如何使用虛擬 DOM 渲染 UI 的。【相關推薦:Redis視頻教程】

常規 DOM 基本上包含一組用樹的形式保存的節點。DOM 中的每個節點代表一個 UI 元素。每當應用程序中出現狀態變更時,該 UI 元素及其所有子元素的相應節點都會在 DOM 樹中更新,然后會觸發 UI 重繪。

在高效的 DOM 樹算法的幫助下,更新節點的速度更快,但重繪的速度很慢,并且當該 DOM 具有大量 UI 元素時,可能會影響性能。因此,在 React 中引入了虛擬 DOM。

這是真實 DOM 的虛擬表示。現在,每當應用程序的狀態有任何變化時,React 不會直接更新真正的 DOM,而是創建一個新的虛擬 DOM。然后 React 會將此新的虛擬 DOM 與之前創建的虛擬 DOM 進行比較,找到有差異的地方(譯者注:也就是找到需要被更新節點),然后進行重繪。

根據這些差異,虛擬 DOM 能更高效地更新真正的 DOM。這樣提高了性能,因為虛擬 DOM 不會簡單地更新 UI 元素及其所有子元素,而是有效地僅更新實際 DOM 中必要且最小的更改。

為什么需要 Memoization?

在上一節中,我們看到了 React 如何使用虛擬 DOM 有效地執行 DOM 更新操作來提高性能。在本節中,我們將介紹一個例子,該例子解釋了為了進一步提高性能而需要使用 Memoization。

我們將創建一個父類,包含一個按鈕,用于遞增名為 count 的變量。父組件還調用了子組件,并向其傳遞參數。我們還在 render 方法中添加了 console.log() 語句:

//Parent.js
class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }

  handleClick = () => {
    this.setState((prevState) => {
      return { count: prevState.count + 1 };
    });
  };

  render() {
    console.log("Parent render");
    return (
      <div className="App">
        <button onClick={this.handleClick}>Increment</button>
        <h3>{this.state.count}</h3>
        <Child name={"joe"} />
      </div>
    );
  }
}

export default Parent;

此示例的完整代碼可在 CodeSandbox 上查看。

我們將創建一個 Child 類,該類接受父組件傳遞的參數并將其顯示在 UI 中:

//Child.js
class Child extends React.Component {
  render() {
    console.log("Child render");
    return (
      <div>
        <h3>{this.props.name}</h3>
      </div>
    );
  }
}

export default Child;

每當我們點擊父組件中的按鈕時,count 值都會更改。由于 state 變化了,因此父組件的 render 方法被執行了。

傳遞給子組件的參數在每次父組件重新渲染時都沒有改變,因此子組件不應重新渲染。然而,當我們運行上面的代碼并繼續遞增 count 時,我們得到了以下輸出:

Parent render
Child render
Parent render
Child render
Parent render
Child render

你可以在這個 sandbox 中體驗上述示例,并查看控制臺的輸出結果。

從輸出中,我們可以看到,當父組件重新渲染時,即使傳遞給子組件的參數保持不變,子組件也會重新渲染。這將導致子組件的虛擬 DOM 與以前的虛擬 DOM 執行差異檢查。由于我們的子組件中沒有變更且重新渲染時的所有 props 都沒有變,所以真正的 DOM 不會被更新。

真正的 DOM 不會進行不必要地更新對性能確實是有好處,但是我們可以看到,即使子組件中沒有實際更改,也會創建新的虛擬 DOM 并執行差異檢查。對于小型 React 組件,這種性能消耗可以忽略不計,但對于大型組件,性能影響會很大。為了避免這種重新渲染和虛擬 DOM 的差異檢查,我們使用 Memoization。

React 中的 Memoization

在 React 應用的上下文中,Memoization 是一種手段,每當父組件重新渲染時,子組件僅在它所依賴的 props 發生變化時才會重新渲染。如果子組件所依賴的 props 中沒有更改,則它不會執行 render 方法,并將返回緩存的結果。由于渲染方法未執行,因此不會有虛擬 DOM 創建和差異檢查,從而實現性能的提升。

現在,讓我們看看如何在類和函數組件中實現 Memoization,以避免這種不必要的重新渲染。

類組件實現 Memoization

為了在類組件中實現 Memoization,我們將使用 React.PureComponent。React.PureComponent 實現了 shouldComponentUpdate(),它對 stateprops 進行了淺比較,并且僅在 props 或 state 發生更改時才重新渲染 React 組件。

將子組件更改為如下所示的代碼:

//Child.js
class Child extends React.PureComponent { // 這里我們把 React.Component 改成了 React.PureComponent
  render() {
    console.log("Child render");
    return (
      <div>
        <h3>{this.props.name}</h3>
      </div>
    );
  }
}

export default Child;

此示例的完整代碼顯示在這個 sandbox 中。

父組件保持不變。現在,當我們在父組件中增加 count 時,控制臺中的輸出如下所示:

Parent render
Child render
Parent render
Parent render

對于首次渲染,它同時調用父組件和子組件的 render 方法。

對于每次增加 count 后的重新渲染,僅調用父組件的 render 函數。子組件不會重新渲染。

函數組件實現 Memoization

為了在函數組件中實現 Memoization,我們將使用 React.memo()。React.memo() 是一個高階組件(HOC),它執行與 PureComponent 類似的工作,來避免不必要的重新渲染。

以下是函數組件的代碼:

//Child.js
export function Child(props) {
  console.log("Child render");
  return (
    <div>
      <h3>{props.name}</h3>
    </div>
  );
}

export default React.memo(Child); // 這里我們給子組件添加 HOC 實現 Memoization

同時還將父組件轉換為了函數組件,如下所示:

//Parent.js
export default function Parent() {
  const [count, setCount] = useState(0);
  const handleClick = () => {
    setCount(count + 1);
  };
  console.log("Parent render");
  return (
    <div>
      <button onClick={handleClick}>Increment</button>
      <h3>{count}</h3>
      <Child name={"joe"} />
    </div>
  );
}

此示例的完整代碼可以在這個 sandbox 中看到。

現在,當我們遞增父組件中的 count 時,以下內容將輸出到控制臺:

Parent render
Child render
Parent render
Parent render
Parent render

React.memo() 存在的問題

在上面的示例中,我們看到,當我們對子組件使用 React.memo() HOC 時,子組件沒有重新渲染,即使父組件重新渲染了。

但是,需要注意的一個小問題是,如果我們將函數作為參數傳遞給子組件,即使在使用 React.memo() 之后,子組件也會重新渲染。讓我們看一個這樣的例子。

我們將更改父組件,如下所示。在這里,我們添加了一個處理函數,并作為參數傳遞給子組件:

//Parent.js
export default function Parent() {
  const [count, setCount] = useState(0);
  const handleClick = () => {
    setCount(count + 1);
  };

  const handler = () => {
    console.log("handler");    // 這里的 handler 函數將會被傳遞給子組件
  };

  console.log("Parent render");
  return (
    <div className="App">
      <button onClick={handleClick}>Increment</button>
      <h3>{count}</h3>
      <Child name={"joe"} childFunc={handler} />
    </div>
  );
}

子組件代碼將保持原樣。我們不會在子組件中使用父組件傳遞來的函數:

//Child.js
export function Child(props) {
  console.log("Child render");
  return (
    <div>
      <h3>{props.name}</h3>
    </div>
  );
}

export default React.memo(Child);

現在,當我們遞增父組件中的 count 時,它會重新渲染并同時重新渲染子組件,即使傳遞的參數中沒有更改。

那么,是什么原因導致子組件重新渲染的呢?答案是,每次父組件重新渲染時,都會創建一個新的 handler 函數并將其傳遞給子組件。現在,由于每次重新渲染時都會重新創建 handle 函數,因此子組件在對 props 進行淺比較時會發現 handler 引用已更改,并重新渲染子組件。

接下來,我們將介紹如何解決此問題。

通過 useCallback() 來避免更多的重復渲染

導致子組件重新渲染的主要問題是重新創建了 handler 函數,這更改了傳遞給子組件的引用。因此,我們需要有一種方法來避免這種重復創建。如果未重新創建 handler 函數,則對 handler 函數的引用不會更改,因此子組件不會重新渲染。

為了避免每次渲染父組件時都重新創建函數,我們將使用一個名為 useCallback() 的 React Hook。Hooks 是在 React 16 中引入的。要了解有關 Hooks 的更多信息,你可以查看 React 的官方 hooks 文檔,或者查看 `React Hooks: How to Get Started & Build Your Own"。

useCallback() 鉤子傳入兩個參數:回調函數和依賴項列表。

以下是 useCallback() 示例:

const handleClick = useCallback(() => {
  //Do something
}, [x,y]);

在這里,useCallback() 被添加到 handleClick() 函數中。第二個參數 [x, y] 可以是空數組、單個依賴項或依賴項列表。每當第二個參數中提到的任何依賴項發生更改時,才會重新創建 handleClick() 函數。

如果 useCallback() 中提到的依賴項沒有更改,則返回作為第一個參數提及的回調函數的 Memoization 版本。我們將更改父組件,以便對傳遞給子組件的處理程序使用 useCallback() 鉤子:

//Parent.js
export default function Parent() {
  const [count, setCount] = useState(0);
  const handleClick = () => {
    setCount(count + 1);
  };

  const handler = useCallback(() => { // 給 handler 函數使用 useCallback()
    console.log("handler");
  }, []);

  console.log("Parent render");
  return (
    <div className="App">
      <button onClick={handleClick}>Increment</button>
      <h3>{count}</h3>
      <Child name={"joe"} childFunc={handler} />
    </div>
  );
}

子組件代碼將保持原樣。

此示例的完整代碼這個 sandbox 中。

當我們在上述代碼的父組件中增加 count 時,我們可以看到以下輸出:

Parent render
Child render
Parent render
Parent render
Parent render

由于我們對父組件中的 handler 使用了 useCallback() 鉤子,因此每次父組件重新渲染時,都不會重新創建 handler 函數,并且會將 handler 的 Memoization 版本傳遞到子組件。子組件將進行淺比較,并注意到 handler 函數的引用沒有更改,因此它不會調用 render 方法。

值得注意的事

Memoization 是一種很好的手段,可以避免在組件的 state 或 props 沒有改變時對組件進行不必要的重新渲染,從而提高 React 應用的性能。你可能會考慮為所有組件添加 Memoization,但這并不一定是構建高性能 React 組件的方法。只有在組件出現以下情況時,才應使用 Memoization:

  • 固定的輸入有固定的輸出時

  • 具有較多 UI 元素,虛擬 DOM 檢查將影響性能

  • 多次傳遞相同的參數

“如何利用Memoization提高React性能”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

玛曲县| 清水县| 哈尔滨市| 大新县| 河东区| 平遥县| 沾益县| 昂仁县| 台东县| 东莞市| 土默特右旗| 上栗县| 磐安县| 哈巴河县| 泸州市| 郓城县| 林芝县| 沾化县| 宾川县| 鲁山县| 临邑县| 南安市| 镇巴县| 手机| 教育| 尚义县| 江陵县| 叙永县| 会宁县| 汨罗市| 河东区| 辛集市| 乌鲁木齐县| 安溪县| 建湖县| 晴隆县| 蓝田县| 互助| 泰和县| 奎屯市| 枣庄市|