React進階—性能優化

React性能優化思路

軟件的性能優化思路就像生活中去看病,大致是這樣的:

  1. 使用工具來分析性能瓶頸(找病根)

  2. 嘗試使用優化技巧解決這些問題(服藥)

  3. 使用工具測試性能是否確實有提升(療效確認)

React性能優化的特殊性

看過《高性能JavaScript》這本書的小伙伴都知道,JavaScipt的語言特性、數據結構和算法、瀏覽器機理、網絡傳輸等都可能導致性能問題。同樣是web實現,跟傳統的技術(如原生js、jQuery)相比, react的性能優化有什么不同呢?

使用jQuery時,要考慮怎么使用選擇器來提高元素查找效率、不要在循環體內進行DOM操作、使用事件委托呀等等。到了React這里,這些東西好像都用不上了。是的,因為React有一個很大的不同點,它實現了虛擬DOM,并且接管了DOM的操作。你不能直接去操作DOM來改變UI,你只能通過改變數據源(props和state)來驅動UI的變化。

說起React的性能分析,還得從它的生命周期和渲染機制說起:

React組件生命周期

圖片描述

當 props 和 state 發生變化時,React會根據shouldComponentUpdate方法來決定是否重新渲染整個組件。

React組件樹渲染機制

圖片描述

父親組件的props 和 state發生變化時,它和它的子組件、孫子組件等所有后代組件都會重新渲染。


綜上所述,可以得出React的性能優化就是圍繞shouldComponentUpdate方法(SCU)來進行的,無外乎兩點:

  1. 縮短SCU方法的執行時間(或者不執行)。

  2. 沒必要的渲染,SCU應該返回false。

React 性能分析工具

Web通用工具:Chrome DevTools

最常用到的是Chrome DevTools的Timeline和Profiles。

  • Timeline工具欄提供了對于在裝載你的Web應用的過程中,時間花費情況的概覽,這些應用包括處理DOM事件, 頁面布局渲染或者向屏幕繪制元素。

  • 通過Timeline發現是腳本問題時,使用Profiles作進一步分析。Profiles可以提供更加詳細的腳本信息。

React特色工具:Perf

Perf 是react官方提供的性能分析工具。Perf最核心的方法莫過于Perf.printWasted(measurements)了,該方法會列出那些沒必要的組件渲染。很大程度上,React的性能優化就是干掉這些無謂的渲染。

有童鞋開發了Chrome擴展程序“React Perf”(戳這里)。相比自己在代碼中插入Perf方法進行分析,這個小工具更加靈活方便,墻裂推薦!

案例分析:TodoList

TodoList的功能很簡單,就是對待辦事項進行增加和刪除操作:
圖片描述

import React, {PropTypes, Component} from 'react';class TodoItem extends Component {static propTypes = {deleteItem: PropTypes.func.isRequired,item: PropTypes.shape({text: PropTypes.string.isRequired,id: PropTypes.number.isRequired,}).isRequired,};deleteItem = ()=>{let id = this.props.item.id;this.props.deleteItem(id);};render() {return (<div><button style={{width: 30}} onClick={this.deleteItem}>X</button>&nbsp;<span>{this.props.item.text}</span></div>);}}class Todos extends Component {// 構造constructor(props) {super(props);// 初始狀態this.state = {items: this.props.initialItems,text: '',};}static propTypes = {initialItems: PropTypes.arrayOf(PropTypes.shape({text: PropTypes.string.isRequired,id: PropTypes.number.isRequired,}).isRequired).isRequired,};addTask = (e)=> {e.preventDefault();this.setState({items: [{id: ID++, text: this.state.text}].concat(this.state.items),text: '',});};deleteItem = (itemId)=> {this.setState({items: this.state.items.filter((item) => item.id !== itemId),});};render() {return (<div><h1>待辦事項</h1><form onSubmit={this.addTask}><input value={this.state.text} onChange={(v)=>{this.setState({text:v.target.value});}}/><button>添加</button></form>{this.state.items.map((item) => {return (<TodoItem key={item.id}item={item}deleteItem={this.deleteItem}/>);})}</div>);}
}let ID = 0;
const items = [];
for (let i = 0; i < 1000; i++) {items.push({id: ID++, text: '事項' + i});
}class TodoList extends Component {render() {return (<Todos initialItems={items}/>);}
}export default TodoList;

在待辦事項輸入框里輸入一個字母,接下來我們以這個行為為例來進行性能分析和優化。

第一次優化

使用Chrome開發者工具的Timeline記錄下這個過程:
clipboard.png

重點關注出現的紅色塊,代表這個行為存在性能問題。從上圖我們可以看出,耗時的Event(keypress)長條花了98.8ms,其中98.5ms用于腳本處理,可見腳本問題是罪魁禍首。

接著,我們使用Profiles來進一步分析腳本問題:
clipboard.png

對Total Time進行降序排列,發現耗時最長的是dispatchEvent,來自react源碼。這時,我們就可以確定是react這一層出現了性能問題。

嗯,輪到Perf出場了:
clipboard.png

上圖表示,有1000次不必要的渲染發生在TodoItem組件上.

打開react面板,我們來看看組件的層次和相應的state、props值:
clipboard.png

TodoItem是Todos的子組件,當我們在輸入框輸入字母“s”時,Todos的state值發生改變時,文章開頭所說的react的渲染機制導致Todos下的1000個TodoItem組件都會重新渲染一次。但是,TodoItem的展現其實沒有任何變化。
從代碼中,我們可以看出,TodoItem組件展現只跟props(deleteItem、item)相關。props沒有變化,TodoItem就沒必要渲染。

所以,我們應該優化下TodoItem的SCU方法:

class TodoItem extends Component {...//在props沒有變化的時候返回false,不重新渲染shouldComponentUpdate(nextState,nextProps) {if(this.props.item == nextProps.item && this.props.deleteItem == nextProps.deleteItem){return false;}return true;}render() {... }}

(PS: TodoItem中的SCU方法,使用的是淺比較,也可以使用PureComponent代替。實際項目中,往往需要使用復雜的深比較,可以考慮使用Immutable.js)

驗證下優化效果,使用Perf測試,發現1000個多余的渲染被干掉了!
再次使用Timeline分析,Event(keypress)耗時從98.5ms降到了26.49ms,性能提升了2.7倍:
clipboard.png

療效還不錯!

第二次優化

通過SCU返回false,我們避免了無謂的渲染。但是,我們還是調用了1000次TodoItem的SCU方法,這也是一筆不小的性能開支。

是否可以不用調用呢?通過合理地規劃組件粒度,可以做到:

//將增加待辦事項抽象成一個組件
class AddItem extends Component{constructor(props) {super(props);this.state = {text:""};}static PropTypes = {addTask:PropTypes.func.isRequired};addTask = (e)=>{e.preventDefault();this.props.addTask(this.state.text);};render(){return (<form onSubmit={this.addTask}><input value={this.state.text} onChange={(v)=>{this.setState({text:v.target.value});}}/><button>添加</button></form>);}
}class Todos extends Component{constructor(props) {super(props);this.state = {items: this.props.initialItems,};}static propTypes = {initialItems: PropTypes.arrayOf(PropTypes.shape({text: PropTypes.string.isRequired,id: PropTypes.number.isRequired,}).isRequired).isRequired,};addTask = (text)=>{this.setState({items: [{id: ID++, text:text}].concat(this.state.items),text: '',});};deleteItem = (itemId)=>{this.setState({items: this.state.items.filter((item) => item.id !== itemId),});};render() {return (<div><h1>待辦事項V3</h1><AddItem addTask={this.addTask}/>{this.state.items.map((item) => {return (<TodoItem key={item.id}item={item}deleteItem={this.deleteItem}/>);})}</div>);}
}

把增加待辦事項抽象成一個AddItem組件。這樣一來,組件樹從原來的
clipboard.png

變成

clipboard.png

輸入信息時觸發變化的text這個state值,被下放到AddItem組件來管理,因此不會導致兄弟組件(TodoItem)的重新渲染。

再次運行Timeline測試,這時Event(keypress)耗時從26.49ms降到了7.98ms,性能提升了2.3倍:
clipboard.png

至此,性能優化完畢~

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/259280.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/259280.shtml
英文地址,請注明出處:http://en.pswp.cn/news/259280.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

內蒙古銀行銀行招聘計算機研究生,內蒙古銀行招聘公告

出國留學網考研報名資訊&#xff1a;內蒙古2015考研報考公告&#xff0c;希望仔細閱讀考研報名公告&#xff0c;及時進行報名&#xff0c;盡量避開報名高峰期!內蒙古2015考研報考公告一、關于報考點的的安排我區共設12個報考點&#xff1a;呼和浩特市招生考試管理中心、內蒙古大…

ubuntu 13.04 telnet 詳細配置

1. sudo vi /etc/xinetd.d/telnet并加入以下內容&#xff1a;# default: on# description: The telnet server serves telnet sessions; it uses \# unencrypted username/password pairs for authentication.service telnet{disable noflags REUSEsocket_type streamwait …

C++定義隱式轉換函數,將類轉換為內部的一個成員變量

C中單參數構造函數若不聲明為explict&#xff0c;在合適的場合可以產生隱式轉換&#xff1a;由成員變量類型轉換為類類型。 下面的代碼展示如何實現反向的轉換&#xff1a; Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/…

2015年百度面經

百度問的是開放性的問題&#xff0c;應該是為了考察你的綜合能力吧&#xff0c;問了兩個問題 一&#xff0c;html&css 涉及的內容 塊元素與行內元素&#xff0c;浮動&#xff0c;清除浮動 1&#xff0c;一個100px的容器&#xff0c;里面塞了一個空的div&#xff0c;這個di…

計算機網頁設計與制作論文,網頁設計與制作論文

二十一世紀是信息化的時代&#xff0c;通過互聯網&#xff0c;就能達到足不出戶便可了解世界的目的。為了加深對互聯網的了解&#xff0c;《網頁設計與制作》這門課的出現就成為了必然。1《網頁設計與制作》現狀問題分析(1)對課程不了解很多學生都有這個困惑&#xff0c;這門課…

mybatis中#{}和${}的區別

http://www.cnblogs.com/davidwang456/p/4929426.html轉載于:https://www.cnblogs.com/xtdxs/p/6666017.html

游標定位:Cursor類

關于 CursorCursor 是每行的集合。使用 moveToFirst() 定位第一行。你必須知道每一列的名稱。你必須知道每一列的數據類型。Cursor 是一個隨機的數據源。所有的數據都是通過下標取得。關于 Cursor 的重要方法&#xff1a;close() 關閉游標&#xff0c;釋放資源copyStringToBuf…

Supervised Descent Method and its Applications to Face Alignment

廣播說明&#xff1a; 進入深度學習時代&#xff0c;如下的方法已經失去可比性&#xff0c;且我們的代碼實現地很粗糙&#xff0c;如果堅持要用&#xff0c;推薦如下代碼 https://github.com/wanglin193/SupervisedDescentMethod &#xff08;看起來作者對sdm實現的不錯&…

導出Excel神器最終版

泛型列表導出Excel&#xff1a; 最近好多導出問題就整這么個玩意共享給大家public class Export{/// <summary>/// 泛型導出Excel/// </summary>/// <param name"strCaption">Excel文件中的標題</param>/// <param name"pList"…

國外計算機課程lab,計算機系統實驗之bomblab

今天剛剛驗收CSAPP實驗3&#xff0c;趁著余溫&#xff0c;記錄一下這個實驗&#xff0c;順便回顧下CSAPP課程的相關知識。實驗目的1.使用gdb工具反匯編出匯編代碼&#xff0c;結合c語言文件找到每個關卡的入口函數。然后分析匯編代碼&#xff0c;分析得到每一關的通關密碼。2.熟…

批量實現ssh免交互認證

因為要部署一批服務器&#xff0c;為了以后管理方便&#xff0c;要進行免密認證。一臺一臺做很費時&#xff0c;腳本又得手動輸密碼。于是上網搜了搜&#xff0c;發現一個非常簡單的免交互認證&#xff0c;不需要入密碼即可完成&#xff01;環境&#xff1a;centos 6.8 虛擬機V…

CSS兼容IE6,IE7,FF的技巧(COPY來的,還沒看)

一、CSS HACK 以下兩種方法幾乎能解決現今所有HACK.翻閱很多資料&#xff0c;已測試可以使用。 1, !important 隨著IE7對!important的支持, !important 方法現在只針對IE6的HACK.(注意寫法.記得該聲明位置需要提前.) PLAIN TEXT CSS: #wrapper { width: 100px!important; /* IE…

計算機復制粘貼教案,信息技術《文本的復制與移動》教案

一、教學內容分析本課是小學信息技術教材四年級下冊第十八課文本的復制與移動。是在學生掌握了文件夾的復制、移動&#xff0c;以及掌握了Word的啟動、退出&#xff0c;在Word中輸入文字并保存等內容之后的又一個知識點&#xff0c;學好這一課為學生以后學習文本的編輯與操作&a…

ajax基礎知識

AJAX 指異步JavaScript及XML&#xff08;Asynchronous JavaScript And XML&#xff09;運用ajax步驟&#xff1a;創建對象&#xff08;注意IE6兼容問題&#xff09;、連接服務器、發送請求、接收返回ajax的readystate屬性&#xff1a;0&#xff1a;表示未初始化1&#xff1a;表…

SDM For Face Alignment 流程介紹及Matlab代碼實現之預處理篇

SDM全稱為 Supervised Descent Method&#xff0c;是一種機器學習的方法&#xff0c;可以被用來做Face Alignment. 下面我們將通過matlab代碼來梳理整個實現的過程。 預處理階段 Input&#xff1a; ../data/lfpw/trainset &#xff08;811張圖片&#xff09; Output: mean_…

Nginx初識

configure腳本&#xff1a;1、cat <EOF >>test.txtEOF多行追加2、uname用于打印當前操作系統的相關信息3、2>/dev/null/dev/null 代表空設備文件 1 表示stdout標準輸出&#xff0c;系統默認值是1&#xff0c;所以">/dev/null"等同于"1>/dev/n…

linux中shell變量$#,$@,$0,$1,$2的含義解釋

linux中shell變量$#,$,$0,$1,$2的含義解釋: 變量說明: $$ Shell本身的PID&#xff08;ProcessID&#xff09; $! Shell最后運行的后臺Process的PID $? 最后運行的命令的結束代碼&#xff08;返回值&#xff09; $- 使用Set命令設定的Flag一覽 $* 所有參數列表。如"$*&quo…

分享25個新鮮出爐的 Photoshop 高級教程

網絡上眾多優秀的 Photoshop 實例教程是提高 Photoshop 技能的最佳學習途徑。今天&#xff0c;我向大家分享25個新鮮出爐的 Photoshop 高級教程&#xff0c;提高你的設計技巧&#xff0c;制作時尚的圖片效果。這些教程可以幫助把你的想法變成現實&#xff0c;并創造新的東西。 …

北京林業大學計算機技術復試,北京林業大學計算機應用技術04年考研復試辦法...

一、復試小組的組成原則和人數計算機應用技術學科碩士研究生復試小組由具有副教授以上(含副教授)職稱的研究生導師和具有博士學位的副教授組成&#xff0c;人數為3~5人。組長有學科負責人擔任&#xff0c;設秘書1名。二、專業課的考核形式和要求專業課的考核形式為筆試和口試。…

SDM For Face Alignment 流程介紹及Matlab代碼實現之訓練篇

SDM 訓練階段的任務如下&#xff1a; 載入標準化的數據&#xff08;包括400*400的正臉及特征點&#xff09;對每一張標準化的圖片&#xff0c;模擬人臉檢測儀&#xff0c;產生10個擾動的人臉框及相應的初始特征點x0。求解Δx,Φ,其中Δxx??x0,x?表示true shape,Φ表示每個特…