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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

React进阶—性能优化

發布時間:2023/12/10 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 React进阶—性能优化 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

React性能優化思路

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

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

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

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

  • 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)來進行的,無外乎兩點:

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

  • 沒必要的渲染,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記錄下這個過程:

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

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

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

    嗯,輪到Perf出場了:

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

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

    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倍:

    療效還不錯!

    第二次優化

    通過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組件。這樣一來,組件樹從原來的

    變成

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

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

    至此,性能優化完畢~

    總結

    以上是生活随笔為你收集整理的React进阶—性能优化的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。