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