日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

光速 React

發(fā)布時(shí)間:2023/12/29 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 光速 React 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

光速 React

Vixlet 團(tuán)隊(duì)優(yōu)化性能的經(jīng)驗(yàn)教訓(xùn)

?

?

在過去一年多,我們?Vixlet?的 web 團(tuán)隊(duì)已經(jīng)著手于一個(gè)激動(dòng)人心的項(xiàng)目:將我們的整個(gè) web 應(yīng)用遷移到 React + Redux 架構(gòu)。對于整個(gè)團(tuán)隊(duì)來說,這是不斷增長的機(jī)遇,而在遷移過程中,我們一路風(fēng)雨兼程。

因?yàn)槲覀兊?web-app 可能有非常大的 feed 視圖,包括成百上千的媒體、文本、視頻、鏈接元素,我們花了相當(dāng)多的時(shí)間尋找能充分利用 React 性能的方法。在這里,我們將分享我們這一路學(xué)到的一些經(jīng)驗(yàn)教訓(xùn)。

聲明:下面講的做法和方法更適用于我們具體應(yīng)用的性能需求。然而,像所有的開發(fā)者建議的那樣,最重要的是要考慮到你的應(yīng)用程序和團(tuán)隊(duì)的實(shí)際需求。React 是一個(gè)開箱即用的框架,所以你可能不需要像我們一樣細(xì)致地優(yōu)化性能。話雖如此,我們還是希望你能在這篇文章里找到一些有用的信息。

基本原理

?

?

向更大的世界邁出第一步。

render() 函數(shù)

一般來說,要盡可能少地在 render 函數(shù)中做操作。如果非要做一些復(fù)雜操作或者計(jì)算,也許你可以考慮使用一個(gè)?memoized?函數(shù)以便于緩存那些重復(fù)的結(jié)果。可以看看?Lodash.memoize,這是一個(gè)開箱即用的記憶函數(shù)。

反過來講,避免在組件的 state 上存儲一些容易計(jì)算的值也很重要。舉個(gè)例子,如果 props 同時(shí)包含?firstName?和?lastName,沒必要在 state 上存一個(gè)?fullName,因?yàn)樗梢院苋菀淄ㄟ^提供的 props 來獲取。如果一個(gè)值可以通過簡單的字符串拼接或基本的算數(shù)運(yùn)算從 props 派生出來,那么沒理由將這些值包含在組件的 state 上。

Prop 和 Reconciliation

重要的是要記住,只要 props(或 state)的值不等于之前的值,React 就會(huì)觸發(fā)重新渲染。如果 props 或者 state 包含一個(gè)對象或者數(shù)組,嵌套值中的任何改變也會(huì)觸發(fā)重新渲染。考慮到這一點(diǎn),你需要注意在每次渲染的生命周期中,創(chuàng)建一個(gè)新的 props 或者 state 都可能無意中導(dǎo)致了性能下降。
PS:譯者對這段保留意見,對象或者數(shù)組只要引用不變,是不會(huì)觸發(fā)rerender的,是我翻譯有誤還是原文的錯(cuò)誤?

例子:?函數(shù)綁定的問題

/* 給 prop 傳入一個(gè)行內(nèi)綁定的函數(shù)(包括 ES6 箭頭函數(shù))實(shí)質(zhì)上是在每次父組件 render 時(shí)傳入一個(gè)新的函數(shù)。 */ render() {return (<div> <a onClick={ () => this.doSomething() }>Bad</a> <a onClick={ this.doSomething.bind( this ) }>Bad</a> </div> ); } /* 應(yīng)該在構(gòu)造函數(shù)中處理函數(shù)綁定并且將已經(jīng)綁定好的函數(shù)作為 prop 的值 */ constructor( props ) { this.doSomething = this.doSomething.bind( this ); //or this.doSomething = (...args) => this.doSomething(...args); } render() { return ( <div> <a onClick={ this.doSomething }>Good</a> </div> ); }

例子:?對象或數(shù)組字面量

/* 對象或者數(shù)組字面量在功能上來看是調(diào)用了 Object.create() 和 new Array()。這意味如果給 prop 傳遞了對象字面量或者數(shù)組字面量。每次render 時(shí) React 會(huì)將他們作為一個(gè)新的值。這在處理 Radium 或者行內(nèi)樣式時(shí)通常是有問題的。 *//* Bad */ // 每次渲染時(shí)都會(huì)為 style 新建一個(gè)對象字面量 render() {return <div style={ { backgroundColor: 'red' } }/> }/* Good */ // 在組件外聲明 const style = { backgroundColor: 'red' };render() {return <div style={ style }/> }

例子?: 注意兜底值字面量

/* 有時(shí)我們會(huì)在 render 函數(shù)中創(chuàng)建一個(gè)兜底的值來避免 undefined 報(bào)錯(cuò)。在這些情況下,最好在組件外創(chuàng)建一個(gè)兜底的常量而不是創(chuàng)建一個(gè)新的字面量。 /* /* Bad */ render() {let thingys = [];// 如果 this.props.thingys 沒有被定義,一個(gè)新的數(shù)組字面量會(huì)被創(chuàng)建if( this.props.thingys ) { thingys = this.props.thingys; } return <ThingyHandler thingys={ thingys }/> } /* Bad */ render() { // 這在功能上和前一個(gè)例子一樣 return <ThingyHandler thingys={ this.props.thingys || [] }/> } /* Good */ // 在組件外部聲明 const NO_THINGYS = []; render() { return <ThingyHandler thingys={ this.props.thingys || NO_THINGYS }/> }

盡可能的保持 Props(和 State)簡單和精簡

理想情況下,傳遞給組件的 props 應(yīng)該是它直接需要的。為了將值傳給子組件而將一個(gè)大的、復(fù)雜的對象或者很多獨(dú)立的 props 傳遞給一個(gè)組件會(huì)導(dǎo)致很多不必要的組件渲染(并且會(huì)增加開發(fā)復(fù)雜性)。

在 Vixlet,我們使用 Redux 作為狀態(tài)容器,所以在我們看來,最理想的是方案在組件層次結(jié)構(gòu)的每一個(gè)層級中使用?react-redux?的?connect()?函數(shù)直接從 store 上獲取數(shù)據(jù)。connect 函數(shù)的性能很好,并且使用它的開銷也非常小。

組件方法

由于組件方法是為組件的每個(gè)實(shí)例創(chuàng)建的,如果可能的話,使用 helper/util 模塊的純函數(shù)或者靜態(tài)類方法。尤其在渲染大量組件的應(yīng)用中會(huì)有明顯的區(qū)別。

進(jìn)階

?

?

在我看來視圖的變化是邪惡的!

shouldComponentUpdate()

React 有一個(gè)生命周期函數(shù)?shouldComponentUpdate()。這個(gè)方法可以根據(jù)當(dāng)前的和下一次的 props 和 state 來通知這個(gè) React 組件是否應(yīng)該被重新渲染。

然而使用這個(gè)方法有一個(gè)問題,開發(fā)者必須考慮到需要觸發(fā)重新渲染的每一種情況。這會(huì)導(dǎo)致邏輯復(fù)雜,一般來說,會(huì)非常痛苦。如果非常需要,你可以使用一個(gè)自定義的shouldComponentUpdate()?方法,但是很多情況下有更好的選擇。

React.PureComponent

React 從 v15 開始會(huì)包含一個(gè) PureComponent 類,它可以被用來構(gòu)建組件。React.PureComponent?聲明了它自己的?shouldComponentUpdate()?方法,它自動(dòng)對當(dāng)前的和下一次的 props 和 state 做一次淺對比。有關(guān)淺對比的更多信息,請參考這個(gè) Stack Overflow:

http://stackoverflow.com/questions/36084515/how-does-shallow-compare-work-in-react

在大多數(shù)情況下,React.PureComponent?是比?React.Component?更好的選擇。在創(chuàng)建新組件時(shí),首先嘗試將其構(gòu)建為純組件,只有組件的功能需要時(shí)才使用?React.Component。

更多信息,請查閱相關(guān)文檔?React.PureComponent。

組件性能分析(在 Chrome 里)

在新版本的 Chrome 里,timeline 工具里有一個(gè)額外的內(nèi)置功能可以顯示哪些 React 組件正在渲染以及他們花費(fèi)的時(shí)間。要啟用此功能,將??react_perf?作為要測試的 URL 的查詢字符串。React 渲染時(shí)間軸數(shù)據(jù)將位于 User Timing 部分。

更多相關(guān)信息,請查閱官方文檔:Profiling Components with Chrome Timeline?。

有用的工具:?why-did-you-update

這是一個(gè)很棒的 NPM 包,他們給 React 添加補(bǔ)丁,當(dāng)一個(gè)組件觸發(fā)了不必要的重新渲染時(shí),它會(huì)在控制臺輸出一個(gè) console 提示。

注意: 這個(gè)模塊在初始化時(shí)可以通過一個(gè)過濾器匹配特定的想要優(yōu)化的組件,否則你的命令行可能會(huì)被垃圾信息填滿,并且可能你的瀏覽器會(huì)掛起或者崩潰,查閱?why-did-you-update 文檔獲取更多詳細(xì)信息。

常見性能陷阱

?

?

setTimeout() 和 setInterval()

在 React 組件中使用?setTimeout()?或者?setInterval()?要十分小心。幾乎總是有更好的選擇,例如 'resize' 和 'scroll' 事件(注意:有關(guān)注意事項(xiàng)請參閱下一節(jié))。

如果你需要使用?setTimeout()?和?setInterval(),你必須遵守下面兩條建議

不要設(shè)置過短的時(shí)間間隔。

當(dāng)心那些小于 100 ms 的定時(shí)器,他們很可能是沒意義的。如果確實(shí)需要一個(gè)更短的時(shí)間,可以使用?window.requestAnimationFrame()。

保留對這些函數(shù)的引用,并且在 unmount 時(shí)取消或者銷毀他們。

setTimeout()?和?setInterval()?都返回一個(gè)延遲函數(shù)的引用,并且需要的時(shí)候可以取消它們。由于這些函數(shù)是在全局作用域執(zhí)行的,他們不在乎你的組件是否存在,這會(huì)導(dǎo)致報(bào)錯(cuò)甚至程序卡死。

注意: 對?window.requestAnimationFrame()?來說也是如此

解決這個(gè)問題最簡答的方法是使用?react-timeout?這個(gè) NPM 包,它提供了一個(gè)可以自動(dòng)處理上述內(nèi)容的高階組件。它將 setTimeout/setInterval 等功能添加到包裝組建的 props 上。(特別感謝 Vixlet 的開發(fā)人員?Carl Pillot?提供這個(gè)方法)

如果你不想引入這個(gè)依賴,并且希望自行解決此問題,你可以使用以下的方法:

// 如何正確取消 timeouts/intervalscompnentDidMount() {this._timeoutId = setTimeout( this.doFutureStuff, 1000 );this._intervalId = setInterval( this.doStuffRepeatedly, 5000 ); } componentWillUnmount() { /* 高級提示:如果操作已經(jīng)完成,或者值未被定義,這些函數(shù)也不會(huì)報(bào)錯(cuò) */ clearTimeout( this._timeoutId ); clearInterval( this._intervalId ); }

如果你使用 requestAnimationFrame() 執(zhí)行的一個(gè)動(dòng)畫循環(huán),可以使用一個(gè)非常相似的解決方案,當(dāng)前代碼要有一點(diǎn)小的修改:

// 如何確保我們的動(dòng)畫循環(huán)在組件消除時(shí)結(jié)束componentDidMount() {this.startLoop(); }componentWillUnmount() {this.stopLoop(); }startLoop() {if( !this._frameId ) { this._frameId = window.requestAnimationFrame( this.loop ); } } loop() { // 在這里執(zhí)行循環(huán)工作 this.theoreticalComponentAnimationFunction() // 設(shè)置循環(huán)的下一次迭代 this.frameId = window.requestAnimationFrame( this.loop ) } stopLoop() { window.cancelAnimationFrame( this._frameId ); // 注意: 不用擔(dān)心循環(huán)已經(jīng)被取消 // cancelAnimationFrame() 不會(huì)拋出異常 }

未去抖頻繁觸發(fā)的事件

某些常見的事件可能會(huì)非常頻繁的觸發(fā),例如?scroll,resize。去抖這些事件是明智的,特別是如果事件處理程序執(zhí)行的不僅僅是基本功能。

Lodash 有?_.debounce?方法。在 NPM 上還有一個(gè)獨(dú)立的?debounce?包.

“但是我真的需要立即反饋 scroll/resize 或者別的事件”

我發(fā)現(xiàn)一種可以處理這些事件并且以高性能的方式進(jìn)行響應(yīng)的方法,那就是在第一次事件觸發(fā)時(shí)啟動(dòng)?requestAnimationFrame()?循環(huán)。然后可以使用?[debounce()](https://lodash.com/docs#debounce)?方法并且將?trailing?這個(gè)配置項(xiàng)設(shè)為?true(這意味著該功能只在頻繁觸發(fā)的事件流結(jié)束后觸發(fā))來取消對值的監(jiān)聽,看看下面這個(gè)例子。

class ScrollMonitor extends React.Component { constructor() { this.handleScrollStart = this.startWatching.bind( this ); this.handleScrollEnd = debounce( this.stopWatching.bind( this ), 100, { leading: false, trailing: true } ); } componentDidMount() { window.addEventListener( 'scroll', this.handleScrollStart ); window.addEventListener( 'scroll', this.handleScrollEnd ); } componentWillUnmount() { window.removeEventListener( 'scroll', this.handleScrollStart ); window.removeEventListener( 'scroll', this.handleScrollEnd ); //確保組件銷毀后結(jié)束循環(huán) this.stopWatching(); } // 如果循環(huán)未開始,啟動(dòng)它 startWatching() { if( !this._watchFrame ) { this.watchLoop(); } } // 取消下一次迭代 stopWatching() { window.cancelAnimationFrame( this._watchFrame ); } // 保持動(dòng)畫的執(zhí)行直到結(jié)束 watchLoop() { this.doThingYouWantToWatchForExampleScrollPositionOrWhatever() this._watchFrame = window.requestAnimationFrame( this.watchLoop ) } }

密集CPU任務(wù)線程阻塞

某些任務(wù)一直是 CPU 密集型的,因此可能會(huì)導(dǎo)致主渲染線程的阻塞。舉幾個(gè)例子,比如非常復(fù)雜的數(shù)學(xué)計(jì)算,迭代非常大的數(shù)組,使用?File?api 進(jìn)行文件讀寫,利用?<canvas>?對圖片進(jìn)行編碼解碼。

在這些情況下,如果有可能最好使用 Web Worker 將這些功能移到另一個(gè)線程上,這樣我們的主渲染線程可以保持順滑。

相關(guān)閱讀

MDN 文章:?Using Web Workers

MDN 文檔:?Worker API

結(jié)語

我們希望上述建議對您能有所幫助。如果沒有 Vixlet 團(tuán)隊(duì)的偉大工作和研究,上述的提示和編程技巧是不可能產(chǎn)出的。他們真的是我曾經(jīng)合作過的最棒的團(tuán)隊(duì)之一。

在你的 React 的征途中保持學(xué)習(xí)和練習(xí),愿原力與你同在!

總結(jié)

以上是生活随笔為你收集整理的光速 React的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。