本文探討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來救援!
首先,讓我們回顧一下我們的目標:
- 編寫函數式組件感覺更好,因為它們是函數。這立即告訴代碼閱讀者它不保存任何狀態。它們很容易從單元測試的角度進行推理。而且它們感覺更簡潔,更像純JavaScript(當然還有JSX)。
- 我們太懶了,不想綁定所有傳遞給子組件的方法。當然,如果方法很複雜,最好重構它們,而不是動態創建它們。動態創建方法意味著我們可以直接在它們使用的地方編寫它們的代碼,我們不必為它們命名,也不必在三個不同的地方提及它們5次。
- 子組件應該只在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(name
和highlighted
鍵)沒有改變。
讓我們更進一步。我們不使用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> }
運行這段代碼後,你會注意到,只有當name
或highlighted
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
// 無狀態組件 function Welcome(props) { return
在React中使用無狀態組件的最佳實踐是什麼?
在React中使用無狀態組件的一些最佳實踐包括:盡可能多地使用它們來提高性能和可讀性;保持它們小巧,專注於單一功能;避免使用狀態或生命週期方法。還建議使用props的解構來編寫更簡潔的代碼。
無狀態組件可以在React中使用生命週期方法嗎?
不可以,無狀態組件不能在React中使用生命週期方法。生命週期方法只適用於類組件。但是,隨著React 16.8中Hooks的引入,你現在可以向函數組件添加狀態和生命週期特性。
React中無狀態組件的局限性是什麼?
雖然無狀態組件有很多優點,但它們也有一些局限性。它們不能使用生命週期方法或管理自己的狀態。但是,這些限制可以通過使用React Hooks來克服。
無狀態組件如何提高代碼的可讀性?
無狀態組件通過簡潔明了來提高代碼的可讀性。它們不管理自己的狀態或使用生命週期方法,這使得它們更簡單、更容易理解。它們還鼓勵使用小型、可重用的組件,這可以使代碼更井然有序,更容易維護。
無狀態組件可以在React中處理事件嗎?
可以,無狀態組件可以在React中處理事件。事件處理程序可以作為props傳遞給無狀態組件。這是一個例子:
function ActionLink(props) { return ( Click me ); }
無狀態組件如何促進代碼的模塊化?
無狀態組件通過促進使用小型、可重用的組件來促進代碼的模塊化。每個組件都可以獨立開發和測試,這使得代碼更易於維護和理解。它還鼓勵關注點分離,其中每個組件負責單一功能。
以上是通過無狀態組件優化反應性能的詳細內容。更多資訊請關注PHP中文網其他相關文章!

JavaScript字符串替換方法詳解及常見問題解答 本文將探討兩種在JavaScript中替換字符串字符的方法:在JavaScript代碼內部替換和在網頁HTML內部替換。 在JavaScript代碼內部替換字符串 最直接的方法是使用replace()方法: str = str.replace("find","replace"); 該方法僅替換第一個匹配項。要替換所有匹配項,需使用正則表達式並添加全局標誌g: str = str.replace(/fi

因此,在這裡,您準備好了解所有稱為Ajax的東西。但是,到底是什麼? AJAX一詞是指用於創建動態,交互式Web內容的一系列寬鬆的技術。 Ajax一詞,最初由Jesse J創造

10款趣味橫生的jQuery遊戲插件,讓您的網站更具吸引力,提升用戶粘性!雖然Flash仍然是開發休閒網頁遊戲的最佳軟件,但jQuery也能創造出令人驚喜的效果,雖然無法與純動作Flash遊戲媲美,但在某些情況下,您也能在瀏覽器中獲得意想不到的樂趣。 jQuery井字棋遊戲 遊戲編程的“Hello world”,現在有了jQuery版本。 源碼 jQuery瘋狂填詞遊戲 這是一個填空遊戲,由於不知道單詞的上下文,可能會產生一些古怪的結果。 源碼 jQuery掃雷遊戲

本教程演示瞭如何使用jQuery創建迷人的視差背景效果。 我們將構建一個帶有分層圖像的標題橫幅,從而創造出令人驚嘆的視覺深度。 更新的插件可與JQuery 1.6.4及更高版本一起使用。 下載

本文演示瞭如何使用jQuery和ajax自動每5秒自動刷新DIV的內容。 該示例從RSS提要中獲取並顯示了最新的博客文章以及最後的刷新時間戳。 加載圖像是選擇

Matter.js是一個用JavaScript編寫的2D剛體物理引擎。此庫可以幫助您輕鬆地在瀏覽器中模擬2D物理。它提供了許多功能,例如創建剛體並為其分配質量、面積或密度等物理屬性的能力。您還可以模擬不同類型的碰撞和力,例如重力摩擦力。 Matter.js支持所有主流瀏覽器。此外,它也適用於移動設備,因為它可以檢測觸摸並具有響應能力。所有這些功能都使其值得您投入時間學習如何使用該引擎,因為這樣您就可以輕鬆創建基於物理的2D遊戲或模擬。在本教程中,我將介紹此庫的基礎知識,包括其安裝和用法,並提供一

本文討論了在瀏覽器中優化JavaScript性能的策略,重點是減少執行時間並最大程度地減少對頁面負載速度的影響。


熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

SublimeText3漢化版
中文版,非常好用

mPDF
mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

記事本++7.3.1
好用且免費的程式碼編輯器

DVWA
Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

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