摘要:對同一層級的子節點進行處理時,會根據進行簡要的復用。或者直接使用,原理一致。
一、從React原理談起 react是什么?
react是用于構建用戶界面的JS框架。因此react只負責解決view層的渲染。
react做了什么?Virtual Dom模型
生命周期管理
setState機制
diff算法
React patch、事件系統
react的 Virtual Dom模型
virtual dom 實際上是對實際Dom的一個抽象,是一個js對象。react所有的表層操作實際上是在操作virtual dom。
經過diff算法會計算出virtual dom的差異,然后將這些差異進行實際的dom操作更新頁面。
react的生命周期 setState機制 理想情況:setState是“異步”的,調用setState只會提交一次state修改到隊列中,不會直接修改this.state。
等到滿足一定條件時,react會合并隊列中的所有修改,觸發一次update流程,更新this.state。
因此setState機制減少了update流程的觸發次數,從而提高了性能。
由于setState會觸發update過程,因此在update過程中必經的生命周期中調用setState會存在循環調用的風險。
另外用于監聽state更新完成,可以使用setState方法的第二個參數,回調函數。在這個回調中讀取this.state就是已經批量更新后的結果。
特殊情況:在實際開發中,setState的表現有時會不同于理想情況。主要是以下兩種。
在mount流程中調用setState。
在setTimeout/Promise回調中調用setState。
在第一種情況下,不會進入update流程,隊列在mount時合并修改并render。
在第二種情況下,setState將不會進行隊列的批更新,而是直接觸發一次update流程。
這是由于setState的兩種更新機制導致的,只有在批量更新模式中,才會是“異步”的。
diff算法diff算法用于計算出兩個virtual dom的差異,是react中開銷最大的地方。
傳統diff算法通過循環遞歸對比差異,算法復雜度為O(n3)。
react diff算法制定了三條策略,將算法復雜度從 O(n3)降低到O(n)。
WebUI中DOM節點跨節點的操作特別少,可以忽略不計。
擁有相同類的組件會擁有相似的DOM結構。擁有不同類的組件會生成不同的DOM結構。
同一層級的子節點,可以根據唯一的ID來區分。
針對這三個策略,react diff實施的具體策略是:
diff對樹進行分層比較,只對比兩棵樹同級別的節點。跨層級移動節點,將會導致節點刪除,重新插入,無法復用。
diff對組件進行類比較,類相同的遞歸diff子節點,不同的直接銷毀重建。diff對同一層級的子節點進行處理時,會根據key進行簡要的復用。兩棵樹中存在相同key的節點時,只會移動節點。
另外,在對比同一層級的子節點時:
diff算法會以新樹的第一個子節點作為起點遍歷新樹,尋找舊樹中與之相同的節點。
如果節點存在,則移動位置。如果不存在,則新建一個節點。
在這過程中,維護了一個字段lastIndex,這個字段表示已遍歷的所有新樹子節點在舊樹中最大的index。
在移動操作時,只有舊index小于lastIndex的才會移動。
這個順序優化方案實際上是基于一個假設,大部分的列表操作應該是保證列表基本有序的。
可以推倒倒序的情況下,子節點列表diff的算法復雜度為O(n2)
由于react中性能主要耗費在于update階段的diff算法,因此性能優化也主要針對diff算法。
1.減少diff算法觸發次數減少diff算法觸發次數實際上就是減少update流程的次數。
正常進入update流程有三種方式:
setState機制在正常運行時,由于批更新策略,已經降低了update過程的觸發次數。
因此,setState優化主要在于非批更新階段中(timeout/Promise回調),減少setState的觸發次數。
常見的業務場景即處理接口回調時,無論數據處理多么復雜,保證最后只調用一次setState。
父組件的render必然會觸發子組件進入update階段(無論props是否更新)。此時最常用的優化方案即為shouldComponentUpdate方法。
最常見的方式為進行this.props和this.state的淺比較來判斷組件是否需要更新。或者直接使用PureComponent,原理一致。
需要注意的是,父組件的render函數如果寫的不規范,將會導致上述的策略失效。
// Bad case // 每次父組件觸發render 將導致傳入的handleClick參數都是一個全新的匿名函數引用。 // 如果this.list 一直都是undefined,每次傳入的默認值[]都是一個全新的Array。 // hitSlop的屬性值每次render都會生成一個新對象 class Father extends Component { onClick() {} render() { returnforceUpdatethis.onClick()} list={this.list || []} hitSlop={{ top: 10, left: 10}}/> } } // Good case // 在構造函數中綁定函數,給變量賦值 // render中用到的常量提取成模塊變量或靜態成員 const hitSlop = {top: 10, left: 10}; class Father extends Component { constructor(props) { super(props); this.onClick = this.onClick.bind(this); this.list = []; } onClick() {} render() { return } }
其中forceUpdate方法調用后將會直接進入componentWillUpdate階段,無法攔截,因此在實際項目中應該棄用。
其他優化策略shouldComponentUpdate
使用shouldComponentUpdate鉤子,根據具體的業務狀態,減少不必要的props變化導致的渲染。如一個不用于渲染的props導致的update。
另外, 也要盡量避免在shouldComponentUpdate 中做一些比較復雜的操作, 比如超大數據的pick操作等。
合理設計state,不需要渲染的state,盡量使用實例成員變量。
不需要渲染的props,合理使用context機制,或公共模塊(比如一個單例服務)變量來替換。
2.正確使用diff算法不使用跨層級移動節點的操作。
對于條件渲染多個節點時,盡量采用隱藏等方式切換節點,而不是替換節點。
盡量避免將后面的子節點移動到前面的操作,當節點數量較多時,會產生一定的性能問題。
未完待續文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/96165.html
摘要:對同一層級的子節點進行處理時,會根據進行簡要的復用。二性能優化方案由于中性能主要耗費在于階段的算法,因此性能優化也主要針對算法。此時最常用的優化方案即為方法。或者直接使用,原理一致。 一、從React原理談起 react是什么? showImg(https://segmentfault.com/img/bVbcYvf?w=1140&h=384); react是用于構建用戶界面的JS框架...
摘要:歡迎來我的個人站點性能優化其他優化瀏覽器關鍵渲染路徑開啟性能優化之旅高性能滾動及頁面渲染優化理論寫法對壓縮率的影響唯快不破應用的個優化步驟進階鵝廠大神用直出實現網頁瞬開緩存網頁性能管理詳解寫給后端程序員的緩存原理介紹年底補課緩存機制優化動 歡迎來我的個人站點 性能優化 其他 優化瀏覽器關鍵渲染路徑 - 開啟性能優化之旅 高性能滾動 scroll 及頁面渲染優化 理論 | HTML寫法...
摘要:工程實踐立足實踐,提示實際水平內聯函數與性能很多關于性能優化的文章都會談及內聯函數,其也是常見的被詬病為拖慢性能表現的元兇之一不過本文卻是打破砂鍋問到底,論證了內聯函數并不一定就會拖慢性能,過度的性能優化反而會有損于應用性能。 showImg(https://segmentfault.com/img/remote/1460000011481413?w=1240&h=825); 前端每周...
閱讀 2032·2021-11-08 13:14
閱讀 2940·2021-10-18 13:34
閱讀 2029·2021-09-23 11:21
閱讀 3591·2019-08-30 15:54
閱讀 1760·2019-08-30 15:54
閱讀 2931·2019-08-29 15:33
閱讀 2581·2019-08-29 14:01
閱讀 1948·2019-08-29 13:52