chinese直男口爆体育生外卖, 99久久er热在这里只有精品99, 又色又爽又黄18禁美女裸身无遮挡, gogogo高清免费观看日本电视,私密按摩师高清版在线,人妻视频毛茸茸,91论坛 兴趣闲谈,欧美 亚洲 精品 8区,国产精品久久久久精品免费

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

React源碼解析

張康康 ? 2019-07-29 18:21 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

作者 | Video++極鏈科技前端Team超凡

整理 | 包包

前言

React 起源于 Facebook 的內(nèi)部項(xiàng)目,是一個(gè)用于構(gòu)建用戶界面的 Javascript 庫。其擁有較高的性能,代碼邏輯非常簡(jiǎn)單,越來越多的人已開始關(guān)注和使用它。

本文希望通過參考 React 源碼,依葫蘆畫瓢地完成React的雛形。來幫助理解其內(nèi)部的實(shí)現(xiàn)原理,知其然更要知其所以然。

虛擬DOM(Virtual DOM)

了解React的都知道,其高效的原因,是因?yàn)镽eact按照頁面的DOM結(jié)構(gòu),利用Javascript在內(nèi)存中構(gòu)建了一套相同結(jié)構(gòu)的虛擬內(nèi)存樹模型,這個(gè)內(nèi)存模型就稱為Virtual DOM。每當(dāng)頁面產(chǎn)生了變化,React的diff算法會(huì)先在內(nèi)存模型中進(jìn)行比對(duì),提取出差異點(diǎn),在將Virtual DOM轉(zhuǎn)化為原生DOM輸出時(shí),按照差異點(diǎn),只patch出有變動(dòng)的部分。

下面是VirtualDOM節(jié)點(diǎn)的定義:

653c1c5046c3420db699815d3dfc4368.png

入口

一切都是從 React.render(, document.body) 開始的,所以先來看看 React是怎么定義的?

React中主要包括:

? render(virtualDom, container) 命令式調(diào)用,一般用于應(yīng)用入口,將虛擬DOM渲染在container容器中;

? createElement(name, props, children) 創(chuàng)建組件時(shí)使用,JSX是其語法糖;

? Component 以ES6中的類式語法聲明時(shí)使用。

createElement(type, props, children)

createElement()的主要作用是根據(jù)給定type創(chuàng)建Virtual DOM節(jié)點(diǎn),JSX是它的語法糖形式;其type參數(shù)可以是原生的html標(biāo)簽名(如:div、tag等),也可以是React組件類或函數(shù)。

組件的實(shí)現(xiàn)

React的所有組件,按照類型可以分為三種:

? 文本展示類型 (TextComponent)

? 原生DOM類型 (DomComponent)

? 自定義類型 (CompositeComponent)

每種類型的組件,都需要處理初始化更新兩種邏輯,具體會(huì)在下面兩個(gè)函數(shù)中實(shí)現(xiàn):

? mountComponent(rootNodeId) 用于處理初始化邏輯

? updateComponent() 用于處理更新邏輯

初始化mountComponent()的實(shí)現(xiàn)

mountComponent()的實(shí)現(xiàn)思路是,根據(jù)virtual Dom對(duì)象生成HTML代碼并返回。

首先定義類型組件的基類Component,它只是簡(jiǎn)單地記錄了傳入的virtualDom對(duì)象,并初始化了組件節(jié)點(diǎn)ID。

cbb4e4d8aa424c038249bd67b03b1f41.png

下面是不同類型組件初始化渲染邏輯的各自實(shí)現(xiàn)。

? TextComponent

作為純展示類型組件,TextComponent 只是簡(jiǎn)單地將需要展示的內(nèi)容,使用標(biāo)簽包裝并返回就可以了。

15304e462a5946f5a2893fcd5aaec1ea.png

? DomComponent

DomComponent類型在處理原生DOM時(shí),需要額外注意一下原生事件部分的處理。

de533d3fe8334d148ec1ced5ee4e7cdb.jpeg

? CompositeComponent

在實(shí)現(xiàn)CompositeComponent類型的初始化渲染邏輯之前,先看一下React組件的定義語法。

cb51a41c4bdc45ecbf34faac4f5cb6fe.png

聲明語法中,App繼承自React.Component,所以我們先來實(shí)現(xiàn)Component這個(gè)類。

這里的 React.Component 不要與上面的 Component 混淆, Component 是不同組件類型的基類,抽象了組件渲染與更新;而React.Component則是Composite這種類型組件聲明時(shí)的基類。

在 React.Component 中,簡(jiǎn)單地聲明了控制數(shù)據(jù)流向的props屬性,以及組件實(shí)例內(nèi)部用于觸發(fā)更新的setState()函數(shù)。

4db60e6ab95242fb811bb32385bb38db.png

在了解了 React.Component 的定義之后,我們回到 CompositeComponent ,開始實(shí)現(xiàn)mountComponent()的邏輯。

首先要了解的是,在composite類型組件中,vDom對(duì)象中的type,指向的是組件類的定義, 因此 mountComponent() 函數(shù)要做的工作,就是使用vDom的props屬性來創(chuàng)建一個(gè)type的實(shí)例。

c78efdd70e424012a2dd3c1b81cd3841.jpeg

思考一下,在JSX語法中,解析器碰到 標(biāo)簽后,就會(huì)去查找到 MyInput 的定義,上面說過JSX只是createElement的語法糖,因此背后調(diào)用的是 React.createElement(MyInput) 。在React規(guī)范中,可以使用類或函數(shù)來聲明組件,因此在 mountComponent() 中使用 new type() ,就可以構(gòu)造出MyInput的實(shí)例了。

更新流程updateComponent()的實(shí)現(xiàn)

實(shí)現(xiàn)完組件的初始化之后,接下來要實(shí)現(xiàn)組件的更新邏輯。

React開放了 setState() 用于組件更新,回顧上面 React.Component 中 setState() 的定義, 實(shí)際調(diào)用的是 this._reactInternalInstance.updateComponent(null, newState) 這個(gè)函數(shù)。而 this._reactInternalInstance指向CompositeComponent,困此更新邏輯交回CompositeComponent.updateComponent()來完成。

? CompositeComponent

Composite類型組件的更新函數(shù),需要處理兩種流程:

  1. 當(dāng)被定義在其它組件的render函數(shù)中時(shí),其包裹組件會(huì)構(gòu)建出新的vDom對(duì)象,根據(jù)傳入新的vDom來處理更新;

  2. 當(dāng)組件內(nèi)部使用setState()觸發(fā)時(shí),根據(jù)新的state來更新;

了解這兩種方式的區(qū)別,可以幫助我們理解下面updateComponent函數(shù)的實(shí)現(xiàn)。

67f7b30f22e3481baa69bb078005760a.jpeg

我們梳理一下更新流程:

  1. 組件在初始化時(shí),記錄下了render組件的實(shí)例,即this._renderedComponent;

  2. 在更新環(huán)節(jié),重新render()得到新的VDomnextRenderVDom;

  3. 通過比對(duì)前后兩個(gè)VDom的type和key,來判斷是執(zhí)行原來_renderedComponent的updateComponent函數(shù),還是重新生成新的組件;

上面使用到了shouldUpdateReactComponent這個(gè)比對(duì)函數(shù),來對(duì)vDom的type和key進(jìn)行比對(duì),其實(shí)現(xiàn)如下:

ea6030e4ab03457d92ec5f482bb33d30.png

上面這個(gè)處理邏輯,就是diff算法的第一個(gè)規(guī)則: 當(dāng)兩個(gè)VDom節(jié)點(diǎn)的類型不一致時(shí),重新構(gòu)建該組件的Virtual DOM樹結(jié)構(gòu)。

? TextComponent Text類型組件作為顆粒度最小的組件,更新邏輯非常簡(jiǎn)單,展示新的文本內(nèi)容即可。

beb0ebaf8c4547ed9ec11bcf7235ff94.png

? DomComponent

因?yàn)閐iff算法的介入,Dom類型的處理邏輯相對(duì)復(fù)雜。 可以分兩步來處理,第一步更新組件輸出的容器DOM上面的屬性;第二步處理子級(jí)DOM。

cdd7a90a1efb4b4b890bf1ff0bdfe9d8.png

_updateProperties()函數(shù)對(duì)比新舊props,完成屬性及事件的處理。 特別注意一下事件處理部分,需要注銷掉原來DOM上注冊(cè)的事件。

822e388e3e40407384564a46fe0c09fe.jpeg

_updateDOMChildren() 用于處理children部分的更新, 這部分的邏輯相對(duì)復(fù)雜,也是diff算法的優(yōu)化點(diǎn)所在。

注:下面的說明中,以名稱中含'children'來標(biāo)識(shí) 集合,'child'指代 集合項(xiàng)。

i. 使用 nextChildrenVDoms 數(shù)據(jù)生成新的nextChildrenComponent;

?DomComponent在初始化流程中,_mountComponent()函數(shù)會(huì)將組件集合保存下來,存入實(shí)例的_renderedChildrenComponent屬性中, 通過遍歷該屬性,可以取得childComponent實(shí)例上的_vDom;

?使用vDom來生成標(biāo)識(shí)索引key,并以childComponent作為索引值,生成childrenComponent的Map結(jié)構(gòu); (對(duì)于Compotite類型,使用vDom.key作為標(biāo)識(shí)索引key; 對(duì)于Text和Dom類型,使用childComponent在childrenComponent中所處的索引位置作為標(biāo)識(shí)索引key);

?使用nextChildrenVDoms生成新nextChildrenComponent的Map結(jié)構(gòu); 在遍歷vDom集合的過程中,會(huì)使用上面的標(biāo)識(shí)索引key生成規(guī)則,來進(jìn)行判定,看是復(fù)用之前的組件實(shí)例觸發(fā)更新,還是創(chuàng)建一個(gè)新的組件;

ii. 經(jīng)過上面一步得到Map結(jié)構(gòu)的prevChildren和nextChildren之后, 會(huì)使用深度遍歷算法,遞歸地比對(duì)樹結(jié)構(gòu)中,相同層級(jí)和位置的兩個(gè)組件,將差異點(diǎn)保存為特定的diff標(biāo)識(shí)結(jié)構(gòu),存入diffQueue隊(duì)列中;

iii. 遍歷diffQueue,按照差異的類型,完成最終HTML DOM的變動(dòng);

首先是_updateDOMChildren()里的的定義。由于在遞歸組件樹的節(jié)點(diǎn)時(shí),存在多次觸發(fā)_updateDOMChildren()的情況; 因此使用_updateDepth變量,在比對(duì)操作前+1,完成后-1,來判定整個(gè)樹的更新是否全部完成,繼而調(diào)用_patch()完成HTML DOM的更新;

54fbb8f38ad24b07b5fc28ff5968097c.png

下面的_diff()中,實(shí)現(xiàn)了更新步驟中的1和2。

2d2aa48a60db4af9b815baff7691eeac.jpeg

值得注意的是_diff過程中l(wèi)astIndex變量的作用,其記錄在遍歷過程中,每次訪問到的prevChildrenComponent中位置最靠后的組件,這是組件更新的一種排序上面的優(yōu)化策略,可以參見這一篇文章當(dāng)中的詳細(xì)介紹:不可思議的react diff。

在計(jì)算出diffQueue的差異隊(duì)列后,在_patch()函數(shù)中完成最終HTML DOM的更新:

66c98874e27c4b4aaf174da6158f5db3.png

總結(jié)

至此,我們實(shí)現(xiàn)了一個(gè)簡(jiǎn)易版本的React框架,完成了組件類的定義、初始化及更新; 并且梳理了核心diff算法。

下面簡(jiǎn)單做一下總結(jié):

? 組件分為3種類型來處理組件的初始化渲染和更新:TextComponent、DomComponent和CompositeComponent;

? virtualDom對(duì)象中,記錄了組件類型type,唯一標(biāo)識(shí)key和屬性集合props;

? 組件是由virtual Dom創(chuàng)建而來,vDom上的type和key用來標(biāo)識(shí)組件實(shí)例的唯一性;

? diff算法的核心,是對(duì)比新舊vDom對(duì)象,來完成部分組件實(shí)例的復(fù)用,并加入了排序優(yōu)化策略。 通過javascript大量計(jì)算的代價(jià),來換取減少頁面DOM重排的消耗,從而提高了渲染性能;

相關(guān)資料:

https://github.com/Matt-Esch/virtual-dom

https://zhuanlan.zhihu.com/p/20346379


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    AT組件無法正確解析bin文件怎么解決?

    通過ESP32接受網(wǎng)絡(luò)數(shù)據(jù),然后寫入MCU中,但是發(fā)現(xiàn)在解析bin文件的時(shí)候,會(huì)自動(dòng)添加字符。應(yīng)該是換行符號(hào)的解析出現(xiàn)了問題。有什么好辦法嗎?
    發(fā)表于 09-28 08:36

    mqtt dns解析失敗是為什么?

    解析域名的ip地址就能正常連上,而直接解析域名就不行,為什么呢
    發(fā)表于 09-16 06:38

    智能小車設(shè)計(jì)源碼和圖紙資料

    智能小車設(shè)計(jì)源碼和圖紙
    發(fā)表于 08-25 15:38 ?0次下載

    【經(jīng)驗(yàn)分享】在Omni3576上編譯Redis-8.0.2源碼,并安裝及性能測(cè)試

    本文首先介紹Redis是什么,然后介紹如何在Omni3576上編譯Redis-8.0.2源碼,以及從源碼編譯、安裝Redis,最后介紹如何在Omni3576上運(yùn)行Redis性能測(cè)試,并與樹莓派5上的結(jié)果進(jìn)行對(duì)比。一、Redis是什么維基百科的介紹是:Redi
    的頭像 發(fā)表于 06-05 08:05 ?665次閱讀
    【經(jīng)驗(yàn)分享】在Omni3576上編譯Redis-8.0.2<b class='flag-5'>源碼</b>,并安裝及性能測(cè)試

    tscircuit - 電路開發(fā)的 React 范式? 用TypeScript、React和 AI工具構(gòu)建電子產(chǎn)品

    用 TypeScript、React 和 AI 工具構(gòu)建電子產(chǎn)品。
    的頭像 發(fā)表于 04-30 18:18 ?1042次閱讀
    tscircuit - 電路開發(fā)的 <b class='flag-5'>React</b> 范式?   用TypeScript、<b class='flag-5'>React</b>和 AI工具構(gòu)建電子產(chǎn)品

    深度解析Linux中的DNS服務(wù)

    dns,Domain Name Server,它的作用是將域名解析為 IP 地址,或者將IP地址解析為域名。
    的頭像 發(fā)表于 04-09 16:13 ?546次閱讀

    STC單片機(jī)聲卡PCB和源碼資料

    STC單片機(jī)聲卡PCB和源碼資料
    發(fā)表于 04-03 11:14 ?0次下載

    CAN報(bào)文流程解析

    CAN報(bào)文流程解析,直流充電樁上的CAN通訊解析過程
    發(fā)表于 03-24 14:03 ?7次下載

    熱門前端框架:引領(lǐng)現(xiàn)代 Web 開發(fā)的潮流

    在當(dāng)今快速發(fā)展的前端開發(fā)領(lǐng)域,熱門前端框架如 React、Vue 和 Angular 等,成為了開發(fā)者構(gòu)建高效、高性能 Web 應(yīng)用的得力工具。它們各自具有獨(dú)特的特點(diǎn)和優(yōu)勢(shì),引領(lǐng)著現(xiàn)代 Web 開發(fā)
    的頭像 發(fā)表于 01-22 10:08 ?762次閱讀

    SSM框架的源碼解析與理解

    的核心是控制反轉(zhuǎn)(IoC)和面向切面編程(AOP)。 源碼解析: Spring的源碼主要分為以下幾個(gè)部分: Bean容器: 負(fù)責(zé)實(shí)例化、配置和組裝對(duì)象。核心接口是 B
    的頭像 發(fā)表于 12-17 09:20 ?1283次閱讀

    源碼開放 智能監(jiān)測(cè)電源管理教程寶典!

    源碼開放,今天我們學(xué)習(xí)的是電源管理系統(tǒng)的核心功能模塊,手把手教你如何通過不同的技術(shù)手段實(shí)現(xiàn)有效的電源管理。
    的頭像 發(fā)表于 12-11 09:26 ?882次閱讀
    <b class='flag-5'>源碼</b>開放  智能監(jiān)測(cè)電源管理教程寶典!

    使用SSR構(gòu)建React應(yīng)用的步驟

    使用SSR(Server-Side Rendering,服務(wù)器端渲染)構(gòu)建React應(yīng)用的步驟通常包括以下幾個(gè)階段: 一、項(xiàng)目初始化與配置 創(chuàng)建React項(xiàng)目 : 可以使用Create React
    的頭像 發(fā)表于 11-18 11:30 ?1086次閱讀

    基于無操作系統(tǒng)的STM32單片機(jī)開發(fā)附源碼

    現(xiàn)在非常多的的MCU性能都還不錯(cuò),同時(shí)用戶也會(huì)去擴(kuò)展一些外部RAM,這樣如果高效便捷的管理這些內(nèi)存是一個(gè)重要話題。 今天給大家分享一份源碼:基于無操作系統(tǒng)的STM32單片機(jī)開發(fā),功能強(qiáng)大,可申請(qǐng)
    的頭像 發(fā)表于 11-15 11:24 ?1707次閱讀

    索尼_imx678_19DV500驅(qū)動(dòng)源碼

    hi3519dv500_IMX678驅(qū)動(dòng)源碼
    發(fā)表于 11-01 10:36 ?6次下載

    Taro鴻蒙技術(shù)內(nèi)幕系列(一):如何將React代碼跑在ArkUI上

    基于 Taro 打造的京東鴻蒙 APP 已跟隨鴻蒙 Next 系統(tǒng)公測(cè),本系列文章將深入解析 Taro 如何實(shí)現(xiàn)使用 React 開發(fā)高性能鴻蒙應(yīng)用的技術(shù)內(nèi)幕。
    的頭像 發(fā)表于 10-25 17:24 ?925次閱讀
    Taro鴻蒙技術(shù)內(nèi)幕系列(一):如何將<b class='flag-5'>React</b>代碼跑在ArkUI上