摘要:有人會說,會用就行了,知道渲染原理有必要么其實渲染原理決定著性能優(yōu)化的方法,只有在了解原理之后,才能完全理解為什么這樣做可以優(yōu)化性能。性能優(yōu)化結合渲染原理,通過實際例子,看看如何優(yōu)化組件。
前言
以下,是我在2018 React Conf 的分享內(nèi)容,希望對大家有所幫助??梢韵仍诠倬W(wǎng)下載我的ppt對照看,效果更佳哦~。
很多人都使用過React,但是很少人能說出它內(nèi)部的渲染原理。有人會說,會用就行了,知道渲染原理有必要么?其實渲染原理決定著性能優(yōu)化的方法,只有在了解原理之后,才能完全理解為什么這樣做可以優(yōu)化性能。正所謂:知其然,然后知其所以然。
廢話不多說,下面我們就開始吧~
本篇文章,將會分為四部分介紹:
JSX如何生成element
當我們寫下一段JSX代碼的時候,react是如何根據(jù)我們的JSX代碼來生成虛擬DOM的組成元素element的。
element如何生成真實DOM節(jié)點
再生成elment之后,react又如何將其轉成瀏覽器的真實節(jié)點。這里會通過介紹首次渲染以及更新渲染的流程來幫助大家理解這個渲染流程。
性能優(yōu)化
結合渲染原理,通過實際例子,看看如何優(yōu)化組件。
React 16異步渲染方案
到目前為止,這些優(yōu)化組件的方法還不能解決什么問題,所以我們需要引入異步渲染,以及異步渲染的原理是什么。
這里是一段寫在render里的jsx代碼。
return ()Hello, This is React Start to learn right now!Right Reserve.
首先,它會經(jīng)過babel編譯成React.createElement的表達式。
return ( React.createElement( "div", { className: "cn" }, React.createElement( Header, null, "Hello, This is React" ), React.createElement( "div", null, "Start to learn right now!" ), "Right Reserve" ) )
這個createElement方法是做什么的呢?
其實從它的名字就可以看出,這是用來生成element的。element在React里,其實就是組成虛擬DOM 樹的節(jié)點,它用來描述你想要在瀏覽器上看到什么。
它的參數(shù)有三個:
1、type -> 標簽
2、attributes -> 標簽屬性,沒有的話,可以為null
3、children -> 標簽的子節(jié)點
這個React.createElement的表達式會在render函數(shù)被調(diào)用的時候執(zhí)行,換句話說,當render函數(shù)被調(diào)用的時候,會返回一個element。
說了那么久element,這個element究竟長什么樣呢?
其實,它就是一個對象,如下:
{ type: "div", props: { className: "cn", children: [ { type: function Header, props: { children: "Hello, This is React" } }, { type: "div", props: { children: "start to learn right now!" } }, "Right Reserve" ] } }
我們來觀察一下這個對象的children,現(xiàn)在有三種類型:
1、string
2、原生DOM節(jié)點
3、React Component - 自定義組件
除了這三種,還有兩種類型:
4、fale ,null, undefined,number
5、數(shù)組 - 使用map方法的時候
這里需要記住一個點:element不一定是Object類型。
二、element如何生成真實節(jié)點順利得到element之后,我們再來看看React是如何把element轉化成真實DOM節(jié)點的。
首先,需要去初始化element,初始化的規(guī)則如下:
以第一條為例:先判斷是否為Object類型,是的話,看它的type是否是原生DOM標簽,是的話,給它創(chuàng)建ReactDOMComponent的實例對象,其他同理。
這時候有的人可能會有所疑問:這些個ReactDOMComponent, ReactCompositeComponentWrapper怎么開發(fā)的時候都沒有見過?
其實這些都是React的私有類,React自己使用,不會暴露給用戶的。它們的常用方法有:mountComponent,updateComponent等。其中mountComponent 用于創(chuàng)建組件,而updateComponent用于用戶更新組件。而我們自定義組件的生命周期函數(shù)以及render函數(shù)都是在這些私有類的方法里被調(diào)用的。
既然這些私有類的方法那么重要我們就先來簡單了解一下吧~
首先是ReactMComponent的mountComponent方法,這個方法的作用是:將element轉成真實DOM節(jié)點,并且插入到相應的container里,然后返回markup(realDOM)。
由此可知ReactDOMComponent的mountComponent是element生成真實節(jié)點的關鍵。
下面看個栗子它是怎么做到的吧。
假設有這樣一個type類型是原生DOM的element:
{ type: "div", props: { className: "cn", children: "Hello world", } }
簡單mountComponent的實現(xiàn):
mountComponent(container) { const domElement = document.createElement(this._currentElement.type); const textNode = document.createTextNode(this._currentElement.props.children); domElement.appendChild(textNode); container.appendChild(domElement); return domElement; }
其實實現(xiàn)的過程很簡單,就是根據(jù)type生成domElement,再將子節(jié)點append進來返回。當然,真實的mountComponent沒有那么簡單,感興趣的可以自己去看源碼啦。
這里需要記住的一個點是:這個類的mountComponent方法會自己操作瀏覽器DOM元素。
講完ReactDOMComponent,再來看看ReactCompositeComponentWrapper。
這個類的mountComponent方法作用是:實例化自定義組件,最后是通過遞歸調(diào)用到ReactDOMComponent的mountComponent方法來得到真實DOM。
注意:也就是說他自己是不直接生成DOM節(jié)點的。
那這個遞歸是一個怎樣的過程呢?我們通過首次渲染來看下。
假設我們有一個Example的組件,它返回
首先從React.render開始,由于我們剛剛說,render函數(shù)被調(diào)用的時候會返回一個element,所以此時返回給我們的element是:
{ type: function Example, props: { children: null } }
由于這個type是一個自定義組件類,此時要初始化的類是ReactCompositeComponentWrapper,接著調(diào)用它的mountComponent方法。這里面會做四件事情,詳情可以看上圖。其中,第二步的render的得到的element為:
{ type: "div", props: { children: "Hello World" } }
由于這個type是一個原生DOM標簽,此時要初始化的類是ReactDOMComponent。接下來它的mountComponent方法就可以幫我們生成對應的DOM節(jié)點放在瀏覽器里啦。
這時候有人可能會有疑問,如果第二步render出來的element 類型也是自定義組件呢?
這時候它就會去調(diào)用ReactCompositeComponentWrapper的mountComponent方法,從而形成了一個遞歸。不管你的自定義組件嵌套多少層,最后總會生成原生dom類型的element,所以最后一定能調(diào)用到ReactDOMComponent的mountComponent方法。
感興趣的可以自己在打斷點看下這個遞歸的過程。
由我打的斷點圖可以看出在ReactCompositeComponent的mountComponent被調(diào)用多次之后,最后調(diào)用到了ReactDOMComponent的mountComponent方法。
到這里,首次渲染的過程就基本講完了:-D。
但是還有一個問題:前面我們說自定義組件的生命周期跟render函數(shù)都是在私有類的方法里被調(diào)用的,現(xiàn)在只看到render函數(shù)被調(diào)用了,那么首次渲染時候生命周期函數(shù) componentWillMount 跟 componentDidMount 在哪被調(diào)用呢?
由圖可知,在第一步得到instance對象之后,就會去看instance.componentWillMount是否有被定義,有的話調(diào)用,而在整個渲染過程結束之后調(diào)用componentDidMount。
以上,就是渲染原理的部分,讓我們來總結以下:
JSX代碼經(jīng)過babel編譯之后變成React.createElement的表達式,這個表達式在render函數(shù)被調(diào)用的時候執(zhí)行生成一個element。
在首次渲染的時候,先去按照規(guī)則初始化element,接著ReactComponentComponentWrapper通過遞歸,最終調(diào)用ReactDOMComponent的mountComponent方法來幫助生成真實DOM節(jié)點。
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/97541.html
摘要:下面我們撇開網(wǎng)絡方面的優(yōu)化,只分析靜態(tài)資源方面的優(yōu)化。不過,也會阻止的構建和延緩網(wǎng)頁渲染。未優(yōu)化正常加載優(yōu)化后異步加載根據(jù)上面的分析,我們可以清楚的認識到,非必要優(yōu)先加載的,選擇異步加載是最優(yōu)選擇。 為什么做優(yōu)化 經(jīng)典問題:白屏時間過長,用戶體驗差產(chǎn)生的原因:網(wǎng)絡問題、關鍵渲染路徑(CRP)問題 怎么做優(yōu)化 如何做好優(yōu)化呢,網(wǎng)上隨便一搜,就有很多優(yōu)化總結,無非就是網(wǎng)絡優(yōu)化、靜態(tài)資源(h...
摘要:下面我們撇開網(wǎng)絡方面的優(yōu)化,只分析靜態(tài)資源方面的優(yōu)化。不過,也會阻止的構建和延緩網(wǎng)頁渲染。未優(yōu)化正常加載優(yōu)化后異步加載根據(jù)上面的分析,我們可以清楚的認識到,非必要優(yōu)先加載的,選擇異步加載是最優(yōu)選擇。 為什么做優(yōu)化 經(jīng)典問題:白屏時間過長,用戶體驗差產(chǎn)生的原因:網(wǎng)絡問題、關鍵渲染路徑(CRP)問題 怎么做優(yōu)化 如何做好優(yōu)化呢,網(wǎng)上隨便一搜,就有很多優(yōu)化總結,無非就是網(wǎng)絡優(yōu)化、靜態(tài)資源(h...
摘要:下面我們撇開網(wǎng)絡方面的優(yōu)化,只分析靜態(tài)資源方面的優(yōu)化。不過,也會阻止的構建和延緩網(wǎng)頁渲染。未優(yōu)化正常加載優(yōu)化后異步加載根據(jù)上面的分析,我們可以清楚的認識到,非必要優(yōu)先加載的,選擇異步加載是最優(yōu)選擇。 為什么做優(yōu)化 經(jīng)典問題:白屏時間過長,用戶體驗差產(chǎn)生的原因:網(wǎng)絡問題、關鍵渲染路徑(CRP)問題 怎么做優(yōu)化 如何做好優(yōu)化呢,網(wǎng)上隨便一搜,就有很多優(yōu)化總結,無非就是網(wǎng)絡優(yōu)化、靜態(tài)資源(h...
摘要:端優(yōu)談談關于前端的緩存的問題我們都知道對頁面進行緩存能夠有利于減少請求發(fā)送,從而達到對頁面的優(yōu)化。而作為一名有追求的前端,勢必要力所能及地優(yōu)化我們前端頁面的性能。這種方式主要解決了淺談前端中的過早優(yōu)化問題過早優(yōu)化是萬惡之源。 優(yōu)化向:單頁應用多路由預渲染指南 Ajax 技術的出現(xiàn),讓我們的 Web 應用能夠在不刷新的狀態(tài)下顯示不同頁面的內(nèi)容,這就是單頁應用。在一個單頁應用中,往往只有一...
閱讀 955·2023-04-25 23:50
閱讀 1954·2021-11-19 09:40
閱讀 598·2019-08-30 13:50
閱讀 2727·2019-08-29 17:11
閱讀 1041·2019-08-29 16:37
閱讀 2986·2019-08-29 12:54
閱讀 2792·2019-08-28 18:17
閱讀 2636·2019-08-26 16:55