在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

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

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

React源碼解析

張康康 ? 2019-07-29 18:21 ? 次閱讀

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

整理 | 包包

前言

React 起源于 Facebook 的內部項目,是一個用于構建用戶界面的 Javascript 庫。其擁有較高的性能,代碼邏輯非常簡單,越來越多的人已開始關注和使用它。

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

虛擬DOM(Virtual DOM)

了解React的都知道,其高效的原因,是因為React按照頁面的DOM結構,利用Javascript在內存中構建了一套相同結構的虛擬內存樹模型,這個內存模型就稱為Virtual DOM。每當頁面產生了變化,React的diff算法會先在內存模型中進行比對,提取出差異點,在將Virtual DOM轉化為原生DOM輸出時,按照差異點,只patch出有變動的部分。

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

653c1c5046c3420db699815d3dfc4368.png

入口

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

React中主要包括:

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

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

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

createElement(type, props, children)

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

組件的實現

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

? 文本展示類型 (TextComponent)

? 原生DOM類型 (DomComponent)

? 自定義類型 (CompositeComponent)

每種類型的組件,都需要處理初始化更新兩種邏輯,具體會在下面兩個函數中實現:

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

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

初始化mountComponent()的實現

mountComponent()的實現思路是,根據virtual Dom對象生成HTML代碼并返回。

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

cbb4e4d8aa424c038249bd67b03b1f41.png

下面是不同類型組件初始化渲染邏輯的各自實現。

? TextComponent

作為純展示類型組件,TextComponent 只是簡單地將需要展示的內容,使用標簽包裝并返回就可以了。

15304e462a5946f5a2893fcd5aaec1ea.png

? DomComponent

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

de533d3fe8334d148ec1ced5ee4e7cdb.jpeg

? CompositeComponent

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

cb51a41c4bdc45ecbf34faac4f5cb6fe.png

聲明語法中,App繼承自React.Component,所以我們先來實現Component這個類。

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

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

4db60e6ab95242fb811bb32385bb38db.png

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

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

c78efdd70e424012a2dd3c1b81cd3841.jpeg

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

更新流程updateComponent()的實現

實現完組件的初始化之后,接下來要實現組件的更新邏輯。

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

? CompositeComponent

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

  1. 當被定義在其它組件的render函數中時,其包裹組件會構建出新的vDom對象,根據傳入新的vDom來處理更新;

  2. 當組件內部使用setState()觸發(fā)時,根據新的state來更新;

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

67f7b30f22e3481baa69bb078005760a.jpeg

我們梳理一下更新流程:

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

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

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

上面使用到了shouldUpdateReactComponent這個比對函數,來對vDom的type和key進行比對,其實現如下:

ea6030e4ab03457d92ec5f482bb33d30.png

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

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

beb0ebaf8c4547ed9ec11bcf7235ff94.png

? DomComponent

因為diff算法的介入,Dom類型的處理邏輯相對復雜。 可以分兩步來處理,第一步更新組件輸出的容器DOM上面的屬性;第二步處理子級DOM。

cdd7a90a1efb4b4b890bf1ff0bdfe9d8.png

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

822e388e3e40407384564a46fe0c09fe.jpeg

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

注:下面的說明中,以名稱中含'children'來標識 集合,'child'指代 集合項。

i. 使用 nextChildrenVDoms 數據生成新的nextChildrenComponent;

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

?使用vDom來生成標識索引key,并以childComponent作為索引值,生成childrenComponent的Map結構; (對于Compotite類型,使用vDom.key作為標識索引key; 對于Text和Dom類型,使用childComponent在childrenComponent中所處的索引位置作為標識索引key);

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

ii. 經過上面一步得到Map結構的prevChildren和nextChildren之后, 會使用深度遍歷算法,遞歸地比對樹結構中,相同層級和位置的兩個組件,將差異點保存為特定的diff標識結構,存入diffQueue隊列中;

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

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

54fbb8f38ad24b07b5fc28ff5968097c.png

下面的_diff()中,實現了更新步驟中的1和2。

2d2aa48a60db4af9b815baff7691eeac.jpeg

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

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

66c98874e27c4b4aaf174da6158f5db3.png

總結

至此,我們實現了一個簡易版本的React框架,完成了組件類的定義、初始化及更新; 并且梳理了核心diff算法。

下面簡單做一下總結:

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

? virtualDom對象中,記錄了組件類型type,唯一標識key和屬性集合props;

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

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

相關資料

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

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


聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規(guī)問題,請聯系本站處理。 舉報投訴
收藏 人收藏

    評論

    相關推薦

    SSM框架的源碼解析與理解

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

    使用SSR構建React應用的步驟

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

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

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

    ESP-BOX 智能藥盒源碼解析(續(xù))

    藥盒基本概述請參考上一篇文章源碼解析主函數部分___main/main.c●初始化NVS:初始化非易失性存儲(NVS),如果需要擦除和重新初始化NVS,會進行相應處理。●檢查百度API密鑰:調用
    的頭像 發(fā)表于 10-10 08:01 ?260次閱讀
    ESP-BOX 智能藥盒<b class='flag-5'>源碼</b><b class='flag-5'>解析</b>(續(xù))

    如何在NXP源碼基礎上適配ELF 1開發(fā)板的PWM功能

    本次源碼適配項目是在NXP i.MX6ULL EVK評估板所搭載的Linux內核源碼(版本為Linux-imx_4.1.15)基礎上進行的,主要目標是通過調整功能接口引腳配置,使其適應ELF 1開發(fā)板。為了深入闡述這一適配過程,我們將以PWM功能的適配作為具體示例,深入
    的頭像 發(fā)表于 09-10 10:00 ?948次閱讀
    如何在NXP<b class='flag-5'>源碼</b>基礎上適配ELF 1開發(fā)板的PWM功能

    ESP32 崩潰后調試信息定位到源碼方法

    arduino 通過調試信息定位出錯源碼
    的頭像 發(fā)表于 08-27 14:29 ?953次閱讀

    ElfBoard技術貼|在NXP源碼基礎上適配ELF 1開發(fā)板的按鍵功能

    ,將以按鍵功能的適配作為具體示例,深入解析整個適配的流程。一、準備工作NXP源碼路徑:ELF1開發(fā)板資料包\07-NXP原廠資料\07-1NXP官方源碼\linux-
    的頭像 發(fā)表于 07-10 09:54 ?626次閱讀
    ElfBoard技術貼|在NXP<b class='flag-5'>源碼</b>基礎上適配ELF 1開發(fā)板的按鍵功能

    UCGUI單片機源碼

    UCGUI單片機源碼
    發(fā)表于 07-04 17:11 ?1次下載

    浙大博導開源飛控planner源碼

    浙大博導開源飛控planner源碼
    發(fā)表于 06-12 11:43 ?4次下載

    labview實例源碼之控壓取樣系統(tǒng)

    labview源碼,包含報表、曲線、通訊等
    發(fā)表于 06-06 11:23 ?1次下載

    什么是源碼源碼有什么作用?源碼組件是什么?源碼可二次開發(fā)嗎?

    源碼,也稱為源程序,是指未編譯的按照一定的程序設計語言規(guī)范書寫的文本文件,是一系列人類可讀的計算機語言指令。
    的頭像 發(fā)表于 05-25 14:55 ?1.6w次閱讀
    什么是<b class='flag-5'>源碼</b>?<b class='flag-5'>源碼</b>有什么作用?<b class='flag-5'>源碼</b>組件是什么?<b class='flag-5'>源碼</b>可二次開發(fā)嗎?

    HarmonyOS開發(fā):【基于命令行(獲取源碼)】

    在Ubuntu環(huán)境下通過以下步驟獲取OpenHarmony源碼
    的頭像 發(fā)表于 04-25 22:08 ?407次閱讀
    HarmonyOS開發(fā):【基于命令行(獲取<b class='flag-5'>源碼</b>)】

    OpenHarmony開發(fā)學習:【源碼下載和編譯】

    本文介紹了如何下載鴻蒙系統(tǒng)源碼,如何一次性配置可以編譯三個目標平臺(`Hi3516`,`Hi3518`和`Hi3861`)的編譯環(huán)境,以及如何將源碼編譯為三個目標平臺的二進制文件。
    的頭像 發(fā)表于 04-14 09:36 ?957次閱讀
    OpenHarmony開發(fā)學習:【<b class='flag-5'>源碼</b>下載和編譯】

    基于Android13的AOSP源碼下載及編譯指南

    AOSP(Android Open Source Project)是Android操作系統(tǒng)的開源項目,通過下載和編譯AOSP源碼,您可以獲得原始的Android系統(tǒng),并進行定制和開發(fā)。本教程將向您介紹如何下載AOSP源碼并進行編譯的步驟。
    的頭像 發(fā)表于 01-17 09:49 ?4013次閱讀
    基于Android13的AOSP<b class='flag-5'>源碼</b>下載及編譯指南

    Apache Doris聚合函數源碼解析

    筆者最近由于工作需要開始調研 Apache Doris,通過閱讀聚合函數代碼切入 Apache Doris 內核,同時也秉承著開源的精神,開發(fā)了 array_agg 函數并貢獻給社區(qū)。筆者通過這篇文章記錄下對源碼的一些理解,同時也方便后面的新人更快速地上手源碼開發(fā)。
    的頭像 發(fā)表于 01-16 09:52 ?1054次閱讀
    Apache Doris聚合函數<b class='flag-5'>源碼</b><b class='flag-5'>解析</b>
    主站蜘蛛池模板: 2020狠狠操| 午夜高清免费在线观看| 诱人的老师bd高清日本在线观看| 国产农村一一级特黄毛片| 日韩毛片免费看| 一级毛片真人免费播放视频| 第四色成人网| 91精品啪国产在线观看免费牛牛| 天天爽夜夜爽一区二区三区| 丁香六月婷婷激情| 精品手机在线视频| 免费看va| 日本暴力喉深到呕吐hd| 特级毛片免费视频观看| 种子天堂| 黄到让你下面湿的视频| 亚洲天堂视频在线观看免费| 欧美中出在线| 8050网午夜| 91在线视频免费| 国产黄色在线视频| 老司机亚洲精品影院在线观看 | 欧美日本一区二区三区道| 婷婷激情四射网| 亚洲第二色| 亚洲精品自拍区在线观看| 手机免费黄色网址| 色停停| 色天使亚洲综合在线观看| 女人张腿让男桶免费视频观看| 拍拍免费视频| 欧美一区福利| 国产精品女人在线观看| 国产激烈床戏无遮挡观看| 亚洲伊人久久综合影院2021| 亚洲黄色小说网站| 天天碰视频| 狼人 成人 综合 亚洲| 久久dvd| 大又大又粗又爽又黄毛片女人| 激情综合激情五月|