搜尋
首頁web前端js教程通過無狀態組件優化反應性能

Optimizing React Performance with Stateless Components

Optimizing React Performance with Stateless Components

本文探討React中的無狀態組件。這類組件不包含this.state = { ... }調用,僅處理傳入的“props”和子組件。

要點總結

  • React中的無狀態組件不包含任何this.state = { … }調用。它們只處理傳入的“props”和子組件,這使得它們更簡單,也更容易從測試的角度進行分析。
  • 無狀態組件可以轉換為函數式組件,這更像是純JavaScript,對所使用的框架依賴性更小。
  • 通過使用React.PureComponent可以優化無狀態組件的性能,它具有內置的shouldComponentUpdate方法,可以對每個prop進行淺比較。
  • Recompose是一個用於React中函數組件和高階組件的實用工具庫。它可以用來渲染函數組件,而不會在props不變時重新渲染。
  • 無狀態組件可以顯著提高React應用程序的性能,因為它們不管理自己的狀態或生命週期方法,從而減少了代碼量和內存消耗。但是,它們也有一些限制,例如不能使用生命週期方法或管理自己的狀態。

基礎知識

import React, { Component } from 'react'

class User extends Component {
  render() {
    const { name, highlighted, userSelected } = this.props
    console.log('Hey User is being rendered for', [name, highlighted])
    return <div>
      <h3 style={{fontStyle: highlighted ? 'italic' : 'normal'}}
         onClick={event => {
           userSelected()
         }}>
        {name}
      </h3>
    </div>
  }
}

代碼運行正常。它非常基礎,但建立了示例。

需要注意的是:

  • 它是無狀態的,沒有this.state = { ... }
  • console.log用於了解其使用情況。特別是在進行性能優化時,如果props實際上沒有改變,則需要避免不必要的重新渲染。
  • 這裡的事件處理程序是“內聯”的。這種語法很方便,因為它的代碼靠近它處理的元素,而且這種語法意味著你不需要進行任何.bind(this)操作。
  • 使用這樣的內聯函數,會有一點性能損失,因為函數必須在每次渲染時創建。稍後將詳細介紹這一點。

展示型組件

我們現在意識到上面的組件不僅是無狀態的,它實際上是Dan Abramov所說的展示型組件。它只是一個名稱,但基本上,它很輕量級,產生一些HTML/DOM,並且不處理任何狀態數據。

所以我們可以把它做成一個函數!這不僅感覺“很酷”,而且也使它不那麼可怕,因為它更容易理解。它接收輸入,並且獨立於環境,總是返回相同的輸出。當然,它“回調”,因為其中一個prop是一個可調用的函數。

讓我們重寫它:

import React, { Component } from 'react'

class User extends Component {
  render() {
    const { name, highlighted, userSelected } = this.props
    console.log('Hey User is being rendered for', [name, highlighted])
    return <div>
      <h3 style={{fontStyle: highlighted ? 'italic' : 'normal'}}
         onClick={event => {
           userSelected()
         }}>
        {name}
      </h3>
    </div>
  }
}

感覺不錯吧?它感覺像是純JavaScript,你可以編寫它而無需考慮你正在使用的框架。

持續重新渲染的問題

假設我們的User組件用在一個隨時間變化狀態的組件中。但是狀態不會影響我們的組件。例如,像這樣:

const User = ({ name, highlighted, userSelected }) => {
  console.log('Hey User is being rendered for', [name, highlighted])
  return <div>
    <h3 style={{fontStyle: highlighted ? 'italic' : 'normal'}}
       onClick={event => {
         userSelected()
       }}>
      {name}
    </h3>
  </div>
}

運行這段代碼,你會注意到,即使什麼都沒有改變,我們的組件也會重新渲染!現在這還不是什麼大問題,但在實際應用中,組件往往會越來越複雜,每一次不必要的重新渲染都會導致網站速度變慢。

如果你現在用react-addons-perf調試這個應用,我肯定你會發現時間浪費在渲染Users->User上了。糟糕!該怎麼辦? !

似乎一切都在表明我們需要使用shouldComponentUpdate來覆蓋React如何認為props不同,而我們確信它們並沒有不同。為了添加React生命週期鉤子,組件需要成為一個類。 。所以我們回到最初的基於類的實現,並添加新的生命週期鉤子方法:

回到類組件

import React, { Component } from 'react'

class Users extends Component {
  constructor(props) {
    super(props)
    this.state = {
      otherData: null,
      users: [{name: 'John Doe', highlighted: false}]
    }
  }

  async componentDidMount() {
    try {
      let response = await fetch('https://api.github.com')
      let data = await response.json()
      this.setState({otherData: data})
    } catch(err) {
      throw err
    }
  }

  toggleUserHighlight(user) {
    this.setState(prevState => ({
      users: prevState.users.map(u => {
        if (u.name === user.name) {
          u.highlighted = !u.highlighted
        }
        return u
      })
    }))
  }

  render() {
    return <div>
      <h1 id="Users">Users</h1>
      {
        this.state.users.map(user => {
          return <User
            name={user.name}
            highlighted={user.highlighted}
            userSelected={() => {
              this.toggleUserHighlight(user)
            }}/>
         })
      }
    </div>
  }
}

注意新增的shouldComponentUpdate方法。這有點難看。我們不僅不能再使用函數,而且還必須手動列出可能更改的props。這涉及到一個大膽的假設,即userSelected函數prop不會改變。這不太可能,但需要注意。

但是請注意,即使包含的App組件重新渲染,這也只渲染一次!所以,這對性能有好處。但是我們能做得更好嗎?

React.PureComponent

從React 15.3開始,有一個新的組件基類。它被稱為PureComponent,它有一個內置的shouldComponentUpdate方法,它對每個prop進行“淺比較”。太棒了!如果我們使用這個,我們可以丟棄我們自定義的shouldComponentUpdate方法,該方法必須列出特定的props。

import React, { Component } from 'react'

class User extends Component {
  render() {
    const { name, highlighted, userSelected } = this.props
    console.log('Hey User is being rendered for', [name, highlighted])
    return <div>
      <h3 style={{fontStyle: highlighted ? 'italic' : 'normal'}}
         onClick={event => {
           userSelected()
         }}>
        {name}
      </h3>
    </div>
  }
}

嘗試一下,你會失望的。它每次都會重新渲染。為什麼? !答案是因為userSelected函數在App的render方法中每次都會重新創建。這意味著當基於PureComponent的組件調用它自己的shouldComponentUpdate()時,它會返回true,因為該函數總是不同的,因為它每次都會被創建。

通常情況下,解決這個問題的方法是在包含組件的構造函數中綁定該函數。首先,如果我們這樣做,這意味著我們必須鍵入方法名5次(而之前是1次):

  • this.userSelected = this.userSelected.bind(this)(在構造函數中)
  • userSelected() { ... }(作為方法定義本身)
  • <user ... userselected="{this.userSelected}"></user>(在定義User組件渲染的地方)

另一個問題是,正如你所看到的,當實際執行userSelected方法時,它依賴於一個閉包。特別是它依賴於來自this.state.users.map()迭代器的作用域變量user

誠然,這個問題有一個解決方案,那就是首先將userSelected方法綁定到this,然後當調用該方法(在子組件中)時,將user(或其名稱)傳遞回去。這裡有一個這樣的解決方案。

recompose來救援!

首先,讓我們回顧一下我們的目標:

  1. 編寫函數式組件感覺更好,因為它們是函數。這立即告訴代碼閱讀者它不保存任何狀態。它們很容易從單元測試的角度進行推理。而且它們感覺更簡潔,更像純JavaScript(當然還有JSX)。
  2. 我們太懶了,不想綁定所有傳遞給子組件的方法。當然,如果方法很複雜,最好重構它們,而不是動態創建它們。動態創建方法意味著我們可以直接在它們使用的地方編寫它們的代碼,我們不必為它們命名,也不必在三個不同的地方提及它們5次。
  3. 子組件應該只在props改變時才重新渲染。對於小型快速的組件來說,這可能無關緊要,但對於實際應用來說,當你有許多這樣的組件時,所有這些額外的渲染都會在可以避免的情況下浪費CPU。

(實際上,我們理想的情況是組件只渲染一次。為什麼React不能為我們解決這個問題呢?那樣的話,“如何使React更快”的博客文章就會減少90%。)

根據文檔,recompose是“一個用於函數組件和高階組件的React實用工具庫。可以把它想像成React的lodash。”。這個庫有很多東西可以探索,但現在我們想渲染我們的函數組件,而不會在props沒有改變時重新渲染。

我們第一次嘗試用recompose.pure重寫它回到函數組件,看起來像這樣:

import React, { Component } from 'react'

class User extends Component {
  render() {
    const { name, highlighted, userSelected } = this.props
    console.log('Hey User is being rendered for', [name, highlighted])
    return <div>
      <h3 style={{fontStyle: highlighted ? 'italic' : 'normal'}}
         onClick={event => {
           userSelected()
         }}>
        {name}
      </h3>
    </div>
  }
}

你可能會注意到,如果你運行這段代碼,User組件仍然會重新渲染,即使props(namehighlighted鍵)沒有改變。

讓我們更進一步。我們不使用recompose.pure,而是使用recompose.onlyUpdateForKeys,它是recompose.pure的一個版本,但你可以明確地指定要關注的prop鍵:

const User = ({ name, highlighted, userSelected }) => {
  console.log('Hey User is being rendered for', [name, highlighted])
  return <div>
    <h3 style={{fontStyle: highlighted ? 'italic' : 'normal'}}
       onClick={event => {
         userSelected()
       }}>
      {name}
    </h3>
  </div>
}

運行這段代碼後,你會注意到,只有當namehighlighted props改變時,它才會更新。如果父組件重新渲染,User組件不會。

萬歲!我們找到了金子!

討論

首先,問問自己是否值得對組件進行性能優化。也許這比它值得的要多。你的組件應該很輕量級,也許你可以將任何昂貴的計算從組件中移出,並將它們移到外部的可記憶化函數中,或者你可以重新組織你的組件,以便在某些數據不可用時不浪費渲染組件。例如,在本例中,你可能不想在fetch完成之前渲染User組件。

以對你來說最方便的方式編寫代碼,然後啟動你的程序,然後從那裡迭代以使其性能更好,這不是一個壞的解決方案。在本例中,為了提高性能,你需要將函數組件的定義從:

import React, { Component } from 'react'

class User extends Component {
  render() {
    const { name, highlighted, userSelected } = this.props
    console.log('Hey User is being rendered for', [name, highlighted])
    return <div>
      <h3 style={{fontStyle: highlighted ? 'italic' : 'normal'}}
         onClick={event => {
           userSelected()
         }}>
        {name}
      </h3>
    </div>
  }
}

…改為…

const User = ({ name, highlighted, userSelected }) => {
  console.log('Hey User is being rendered for', [name, highlighted])
  return <div>
    <h3 style={{fontStyle: highlighted ? 'italic' : 'normal'}}
       onClick={event => {
         userSelected()
       }}>
      {name}
    </h3>
  </div>
}

理想情況下,與其展示繞過問題的方法,最好的解決方案是React的一個新補丁,它對shallowEqual進行了巨大的改進,能夠“自動”地識別正在傳入和比較的是一個函數,並且僅僅因為它不相等並不意味著它實際上不同。

承認!對於不得不處理在構造函數中綁定方法和每次重新創建的內聯函數,存在一種折中的替代方案。那就是公共類字段。它是Babel中的一個第二階段特性,所以你的設置很可能支持它。例如,這裡有一個使用它的分支,它不僅更短,而且現在也意味著我們不需要手動列出所有非函數props。這個解決方案必須放棄閉包。儘管如此,當需要時,了解和意識到recompose.onlyUpdateForKeys仍然是好的。

更多關於React的信息,請查看我們的課程《React The ES6 Way》。

本文由Jack Franklin同行評審。感謝所有SitePoint的同行評審者,使SitePoint的內容盡善盡美!

關於使用無狀態組件優化React性能的常見問題

React中狀態組件和無狀態組件的主要區別是什麼?

狀態組件(也稱為類組件)維護關於組件狀態隨時間變化的內存。它們負責組件的行為和渲染方式。另一方面,無狀態組件(也稱為函數組件)沒有自己的狀態。它們以props的形式從父組件接收數據並渲染它。它們主要負責組件的UI部分。

如何使用無狀態組件優化我的React應用程序的性能?

無狀態組件可以顯著提高React應用程序的性能。由於它們不管理自己的狀態或生命週期方法,因此它們的代碼更少,這使得它們更容易理解和測試。它們還可以減少應用程序消耗的內存量。為了優化性能,你可以盡可能地將狀態組件轉換為無狀態組件,並使用React的PureComponent來避免不必要的重新渲染。

什麼是PureComponent,它如何幫助優化React性能?

PureComponent是一種特殊的React組件,可以幫助優化應用程序的性能。它使用淺層prop和狀態比較來實現shouldComponentUpdate生命週期方法。這意味著它只有在狀態或props發生變化時才會重新渲染,這可以顯著減少不必要的重新渲染並提高性能。

如何將React中的狀態組件轉換為無狀態組件?

將狀態組件轉換為無狀態組件包括從組件中刪除任何狀態或生命週期方法。組件應該只通過props接收數據並渲染它。這是一個例子:

// 狀態組件 class Welcome extends React.Component { render() { return

Hello, {this.props.name}
; } }

// 無狀態組件 function Welcome(props) { return

Hello, {props.name}
; }

在React中使用無狀態組件的最佳實踐是什麼?

在React中使用無狀態組件的一些最佳實踐包括:盡可能多地使用它們來提高性能和可讀性;保持它們小巧,專注於單一功能;避免使用狀態或生命週期方法。還建議使用props的解構來編寫更簡潔的代碼。

無狀態組件可以在React中使用生命週期方法嗎?

不可以,無狀態組件不能在React中使用生命週期方法。生命週期方法只適用於類組件。但是,隨著React 16.8中Hooks的引入,你現在可以向函數組件添加狀態和生命週期特性。

React中無狀態組件的局限性是什麼?

雖然無狀態組件有很多優點,但它們也有一些局限性。它們不能使用生命週期方法或管理自己的狀態。但是,這些限制可以通過使用React Hooks來克服。

無狀態組件如何提高代碼的可讀性?

無狀態組件通過簡潔明了來提高代碼的可讀性。它們不管理自己的狀態或使用生命週期方法,這使得它們更簡單、更容易理解。它們還鼓勵使用小型、可重用的組件,這可以使代碼更井然有序,更容易維護。

無狀態組件可以在React中處理事件嗎?

可以,無狀態組件可以在React中處理事件。事件處理程序可以作為props傳遞給無狀態組件。這是一個例子:

function ActionLink(props) { return ( Click me ); }

無狀態組件如何促進代碼的模塊化?

無狀態組件通過促進使用小型、可重用的組件來促進代碼的模塊化。每個組件都可以獨立開發和測試,這使得代碼更易於維護和理解。它還鼓勵關注點分離,其中每個組件負責單一功能。

以上是通過無狀態組件優化反應性能的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
JavaScript框架:為現代網絡開發提供動力JavaScript框架:為現代網絡開發提供動力May 02, 2025 am 12:04 AM

JavaScript框架的強大之處在於簡化開發、提升用戶體驗和應用性能。選擇框架時應考慮:1.項目規模和復雜度,2.團隊經驗,3.生態系統和社區支持。

JavaScript,C和瀏覽器之間的關係JavaScript,C和瀏覽器之間的關係May 01, 2025 am 12:06 AM

引言我知道你可能會覺得奇怪,JavaScript、C 和瀏覽器之間到底有什麼關係?它們之間看似毫無關聯,但實際上,它們在現代網絡開發中扮演著非常重要的角色。今天我們就來深入探討一下這三者之間的緊密聯繫。通過這篇文章,你將了解到JavaScript如何在瀏覽器中運行,C 在瀏覽器引擎中的作用,以及它們如何共同推動網頁的渲染和交互。 JavaScript與瀏覽器的關係我們都知道,JavaScript是前端開發的核心語言,它直接在瀏覽器中運行,讓網頁變得生動有趣。你是否曾經想過,為什麼JavaScr

node.js流帶打字稿node.js流帶打字稿Apr 30, 2025 am 08:22 AM

Node.js擅長於高效I/O,這在很大程度上要歸功於流。 流媒體匯總處理數據,避免內存過載 - 大型文件,網絡任務和實時應用程序的理想。將流與打字稿的類型安全結合起來創建POWE

Python vs. JavaScript:性能和效率注意事項Python vs. JavaScript:性能和效率注意事項Apr 30, 2025 am 12:08 AM

Python和JavaScript在性能和效率方面的差異主要體現在:1)Python作為解釋型語言,運行速度較慢,但開發效率高,適合快速原型開發;2)JavaScript在瀏覽器中受限於單線程,但在Node.js中可利用多線程和異步I/O提升性能,兩者在實際項目中各有優勢。

JavaScript的起源:探索其實施語言JavaScript的起源:探索其實施語言Apr 29, 2025 am 12:51 AM

JavaScript起源於1995年,由布蘭登·艾克創造,實現語言為C語言。 1.C語言為JavaScript提供了高性能和系統級編程能力。 2.JavaScript的內存管理和性能優化依賴於C語言。 3.C語言的跨平台特性幫助JavaScript在不同操作系統上高效運行。

幕後:什麼語言能力JavaScript?幕後:什麼語言能力JavaScript?Apr 28, 2025 am 12:01 AM

JavaScript在瀏覽器和Node.js環境中運行,依賴JavaScript引擎解析和執行代碼。 1)解析階段生成抽象語法樹(AST);2)編譯階段將AST轉換為字節碼或機器碼;3)執行階段執行編譯後的代碼。

Python和JavaScript的未來:趨勢和預測Python和JavaScript的未來:趨勢和預測Apr 27, 2025 am 12:21 AM

Python和JavaScript的未來趨勢包括:1.Python將鞏固在科學計算和AI領域的地位,2.JavaScript將推動Web技術發展,3.跨平台開發將成為熱門,4.性能優化將是重點。兩者都將繼續在各自領域擴展應用場景,並在性能上有更多突破。

Python vs. JavaScript:開發環境和工具Python vs. JavaScript:開發環境和工具Apr 26, 2025 am 12:09 AM

Python和JavaScript在開發環境上的選擇都很重要。 1)Python的開發環境包括PyCharm、JupyterNotebook和Anaconda,適合數據科學和快速原型開發。 2)JavaScript的開發環境包括Node.js、VSCode和Webpack,適用於前端和後端開發。根據項目需求選擇合適的工具可以提高開發效率和項目成功率。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

將Eclipse與SAP NetWeaver應用伺服器整合。

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

SecLists

SecLists

SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

強大的PHP整合開發環境