本篇文章主要的講述了關於react框架的原理詳解,下面還有很多關於react的深入了解,現在就讓我們一起來看看這篇文章吧
#React 搞了2年多了,對這門框架可謂又愛又恨,它的優勢大家都熟知,但是缺點也漸漸暴露,一個大型項目裡,配合
Redux
、ReactRouter
等三方框架後,結合複雜的業務程式碼量會變得非常大(前端程式碼常常是以前的1.5倍)。如果前期底層設計得不好,時常面臨開發效率低的問題。下面歸納了一些React框架的核心概念,希望對大家有幫助:
React diff 演算法
React的diff
演算法是Virtual DOM
之所以任性的最大依仗,大家知道頁面的性能一般是由渲染速度和渲染次數決定,如何最大程度地利用diff
演算法進行開發?我們先看看它的原理。
傳統diff 演算法
計算一棵樹形結構轉換成另一棵樹形結構的最少操作,傳統diff 演算法透過循環遞歸對節點進行依次對比,效率低下,演算法複雜度達到O(n^3)
,其中n 是樹中節點的總數。也就是說如果要展示1000個節點,就要依序執行上十億次的比較。這個效能消耗對對於前端專案來說是不可接受的。
核心演算法
如上所見,傳統 diff 演算法的複雜度為 O(n^3)
,顯然這是無法滿足效能要求的。而React
透過制定大膽的策略,將 O(n^3)
複雜度的問題轉換成 O(n)
複雜度的問題。他是怎麼做到的?
tree diff
Web UI 中 DOM 節點跨層級的移動操作特別少,可以忽略不計。 React 對樹的演算法進行了簡潔明了的最佳化,即對樹進行分層比較,兩棵樹只會對同一層次的節點進行比較。如下圖所示:
React 透過updateDepth 對Virtual DOM 樹進行層級控制,只會對相同顏色方塊內的DOM 節點進行比較,即同一個父節點下的所有子節點。當發現節點已經不存在,則該節點及其子節點會被完全刪除掉,不會用於進一步的比較。這樣只需要對樹進行一次遍歷,便能完成整個 DOM 樹的比較。
// tree diff算法实现updateChildren: function(nextNestedChildrenElements, transaction, context) { updateDepth++; var errorThrown = true; try { this._updateChildren(nextNestedChildrenElements, transaction, context); errorThrown = false; } finally { updateDepth--; if (!updateDepth) { if (errorThrown) { clearQueue(); } else { processQueue(); } } } }
為什麼要減少DOM節點的跨層級運算?
如下圖,A 節點(包括其子節點)整個被移動到D 節點下,由於React 只會簡單的考慮同層級節點的位置變換,而對於不同層級的節點,只有建立和刪除操作。當根節點發現子節點中 A 消失了,就會直接銷毀 A;當 D 發現多了一個子節點 A,則會建立新的 A(包括子節點)作為其子節點。此時,React diff
的執行情況:create A -> create B -> create C -> delete A。
由此可發現,當節點跨層級移動時,並不會出現想像中的移動操作,而是以A 為根節點的樹被整個重新創建,這是一種影響React
效能的操作。
component diff
擁有相同類別的兩個元件將會產生相似的樹狀結構,擁有不同類別的兩個元件將會產生不同的樹形結構。
如果是相同類型的元件,請依照原始策略繼續比較
virtual DOM tree
。如果不是,則將該元件判斷為
dirty component
,從而替換整個元件下的所有子節點。對於同一類型的元件,有可能其
Virtual DOM
沒有任何變化,如果能夠確切的知道這點那可以節省大量的diff 運算時間,因此React
允許使用者透過shouldComponentUpdate()
來判斷該元件是否需要進行diff。
如上圖,當component D
改變為component G
時,即使這兩個component
結構相似,一旦React
判斷D 和G 是不同類型的元件,就不會比較二者的結構,而是直接刪除component D
,重新建立component G
以及其子節點。雖然當兩個component
是不同類型但結構相似時,React diff
會影響效能,但正如React
官方部落格所言:不同類型的component
是很少存在相似DOM tree
的機會,因此這種極端因素很難在實現開發過程中造成重大影響的。
element diff
對於同一層級的一組子節點,它們可以透過唯一 id 來區分。 React 提出最佳化策略:讓開發者對同一層級的同組子節點,增加唯一 key 進行區分,雖然只是小小的改動,但效能上卻發生了翻天覆地的變化!
新舊集合所包含的節點,如下圖所示,新舊集合進行diff 差異化對比,透過key 發現新舊集合中的節點都是相同的節點,因此無需進行節點刪除和創建,只需要將舊集合中節點的位置進行移動,更新為新集合中節點的位置,此時React 給出的diff 結果為:B、D 不做任何操作,A、C 進行移動操作,即可。
開發建議
(1)[基於tree diff] 開發元件時,保持穩定的DOM結構有助於維持整體的性能。換而言之,盡可能少地動態操作DOM結構,尤其是移動操作。當節點數過大或頁面更新次數過多時,頁面卡頓的現像比較明顯。可以透過 CSS 隱藏或顯示節點,而不是真的移除或新增 DOM 節點。
(2)[基於component diff] 開發元件時,請注意使用 shouldComponentUpdate()
來減少元件不必要的更新。除此之外,對於類似的結構應該盡量封裝成元件,既減少程式碼量,又能減少component diff
的效能消耗。
(3)[基於element diff] 對於列表結構,盡量減少類似將最後一個節點移動到列表首部的操作,當節點數量過大或更新操作過於頻繁時,在一定程度上會影響React 的渲染效能。
React Lifecycle
React的生命週期具體可分為四種情況:
當首次裝載元件時,依序執行
getDefaultProps
、getInitialState
、componentWillMount
、render
和componentDidMount
;##當卸載元件時,執行
componentWillUnmount
;當重新裝載元件時,此時會依序執行
getInitialState
、componentWillMount
、render
和componentDidMount
,但不執行getDefaultProps
;#當再次渲染元件時,元件接受到更新狀態,此時依序執行
componentWillReceiveProps
、shouldComponentUpdate
、componentWillUpdate
、render
和componentDidUpdate
。
React元件的3種狀態
狀態一:MOUNTING
mountComponent
負責管理生命週期中的 getInitialState
、componentWillMount
、render
和componentDidMount
。
狀態二:RECEIVE_PROPS
updateComponent
負責管理生命週期中的componentWillReceiveProps
、shouldComponentUpdate
、componentWillUpdate
、render
和componentDidUpdate
。
狀態三:UNMOUNTING
unmountComponent
負責管理生命週期中的 componentWillUnmount
。 (想看更多就到PHP中文網React參考手冊欄位學習)
先將狀態設定為UNMOUNTING
,若有componentWillUnmount
,則執行;如果此時在componentWillUnmount
中呼叫setState
,是不會觸發reRender
。更新狀態為 NULL
,完成元件卸載作業。實作程式碼如下:
// 卸载组件unmountComponent: function() { // 设置状态为 UNMOUNTING this._compositeLifeCycleState = CompositeLifeCycle.UNMOUNTING; // 如果存在 componentWillUnmount,则触发 if (this.componentWillUnmount) { this.componentWillUnmount(); } // 更新状态为 null this._compositeLifeCycleState = null; this._renderedComponent.unmountComponent(); this._renderedComponent = null; ReactComponent.Mixin.unmountComponent.call(this); }
React生命周期总结
生命周期 | 调用次数 | 能否使用setState() |
---|---|---|
getDefaultProps | 1 | 否 |
getInitialState | 1 | 否 |
componentWillMount | 1 | 是 |
render | >=1 | 否 |
componentDidMount | 1 | 是 |
componentWillReceiveProps | >=0 | 是 |
shouldComponentUpdate | >=0 | 否 |
componentWillUpdate | >=0 | 否 |
componentDidUpdate | >=0 | 否 |
componentWillUnmount | 1 | 否 |
componentDidUnmount | 1 | 否 |
setState实现机制
setState
是React
框架的核心方法之一,下面介绍一下它的原理:
// 更新 statesetState: function(partialState, callback) { // 合并 _pendingState this.replaceState( assign({}, this._pendingState || this.state, partialState), callback ); },
当调用 setState
时,会对 state
以及 _pendingState
更新队列进行合并操作,但其实真正更新 state
的幕后黑手是replaceState
。
// 更新 statereplaceState: function(completeState, callback) { validateLifeCycleOnReplaceState(this); // 更新队列 this._pendingState = completeState; // 判断状态是否为 MOUNTING,如果不是,即可执行更新 if (this._compositeLifeCycleState !== CompositeLifeCycle.MOUNTING) { ReactUpdates.enqueueUpdate(this, callback); } },
replaceState
会先判断当前状态是否为 MOUNTING
,如果不是即会调用 ReactUpdates.enqueueUpdate
执行更新。
当状态不为 MOUNTING
或 RECEIVING_PROPS
时,performUpdateIfNecessary
会获取 _pendingElement
、_pendingState
、_pendingForceUpdate
,并调用 updateComponent
进行组件更新。
// 如果存在 _pendingElement、_pendingState、_pendingForceUpdate,则更新组件performUpdateIfNecessary: function(transaction) { var compositeLifeCycleState = this._compositeLifeCycleState; // 当状态为 MOUNTING 或 RECEIVING_PROPS时,则不更新 if (compositeLifeCycleState === CompositeLifeCycle.MOUNTING || compositeLifeCycleState === CompositeLifeCycle.RECEIVING_PROPS) { return; } var prevElement = this._currentElement; var nextElement = prevElement; if (this._pendingElement != null) { nextElement = this._pendingElement; this._pendingElement = null; } // 调用 updateComponent this.updateComponent( transaction, prevElement, nextElement ); }
如果在
shouldComponentUpdate
或componentWillUpdate
中调用setState
,此时的状态已经从RECEIVING_PROPS -> NULL
,则performUpdateIfNecessary
就会调用updateComponent
进行组件更新,但updateComponent
又会调用shouldComponentUpdate
和componentWillUpdate
,因此造成循环调用,使得浏览器内存占满后崩溃。
开发建议
不建议在 getDefaultProps
、getInitialState
、shouldComponentUpdate
、componentWillUpdate
、render
和 componentWillUnmount
中调用 setState,特别注意:不能在 shouldComponentUpdate
和 componentWillUpdate
中调用 setState
,会导致循环调用。
本篇文章到这就结束了(想看更多就到PHP中文网React使用手册栏目中学习),有问题的可以在下方留言提问。
以上是React框架有哪些演算法? react框架的演算法詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

Node.js擅長於高效I/O,這在很大程度上要歸功於流。 流媒體匯總處理數據,避免內存過載 - 大型文件,網絡任務和實時應用程序的理想。將流與打字稿的類型安全結合起來創建POWE

Python和JavaScript在性能和效率方面的差異主要體現在:1)Python作為解釋型語言,運行速度較慢,但開發效率高,適合快速原型開發;2)JavaScript在瀏覽器中受限於單線程,但在Node.js中可利用多線程和異步I/O提升性能,兩者在實際項目中各有優勢。

JavaScript起源於1995年,由布蘭登·艾克創造,實現語言為C語言。 1.C語言為JavaScript提供了高性能和系統級編程能力。 2.JavaScript的內存管理和性能優化依賴於C語言。 3.C語言的跨平台特性幫助JavaScript在不同操作系統上高效運行。

JavaScript在瀏覽器和Node.js環境中運行,依賴JavaScript引擎解析和執行代碼。 1)解析階段生成抽象語法樹(AST);2)編譯階段將AST轉換為字節碼或機器碼;3)執行階段執行編譯後的代碼。

Python和JavaScript的未來趨勢包括:1.Python將鞏固在科學計算和AI領域的地位,2.JavaScript將推動Web技術發展,3.跨平台開發將成為熱門,4.性能優化將是重點。兩者都將繼續在各自領域擴展應用場景,並在性能上有更多突破。

Python和JavaScript在開發環境上的選擇都很重要。 1)Python的開發環境包括PyCharm、JupyterNotebook和Anaconda,適合數據科學和快速原型開發。 2)JavaScript的開發環境包括Node.js、VSCode和Webpack,適用於前端和後端開發。根據項目需求選擇合適的工具可以提高開發效率和項目成功率。

是的,JavaScript的引擎核心是用C語言編寫的。 1)C語言提供了高效性能和底層控制,適合JavaScript引擎的開發。 2)以V8引擎為例,其核心用C 編寫,結合了C的效率和麵向對象特性。 3)JavaScript引擎的工作原理包括解析、編譯和執行,C語言在這些過程中發揮關鍵作用。

JavaScript是現代網站的核心,因為它增強了網頁的交互性和動態性。 1)它允許在不刷新頁面的情況下改變內容,2)通過DOMAPI操作網頁,3)支持複雜的交互效果如動畫和拖放,4)優化性能和最佳實踐提高用戶體驗。


熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

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

EditPlus 中文破解版
體積小,語法高亮,不支援程式碼提示功能

Safe Exam Browser
Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

WebStorm Mac版
好用的JavaScript開發工具

PhpStorm Mac 版本
最新(2018.2.1 )專業的PHP整合開發工具