按部就班DIY一個react(一)

前言

假設我大學本科畢業論文的課題是[依據react現有的思想DIY一個react],我會怎麼實現呢?做爲一個react老用戶的我,我經常有這樣的疑問。那好,如今,我就在這根據現有的react概念和思想,按部就班地DIY一個簡單版的react。一來,爲本身立下一個react研究進程的里程碑;二來,跟你們交流交流。javascript

正文

以下,我將這個按部就班的DIY過程細分爲四個步驟:html

  1. 設計virtual DOM的數據結構。
  2. 實現從virtual DOM -> real DOM的映射。
  3. 實現整樹映射過程當中協調。
  4. 實現子樹映射過程的協調。

這四個步驟一路下來,確定會遇到不少新的概念。不要緊,概念是人定義的,只要是人定義的,就能夠解釋。何況,react的官方也解釋過的。不過,只不過我會根據個人理解來解釋。java

一. 設計virtual DOM的數據結構

我的以爲,第一個提出「virtual DOM」概念的人想必對DOM是有十分深刻的瞭解的。聰明的人一看就知道,virtual DOM這個概念確定是相對(real)DOM這個概念而言的。因此,想要理解virtual DOM,咱們不妨從理解(real)DOM開始。什麼是DOM?node

javascrit犀牛書裏面是這樣說的:python

文檔對象模型(DOM)是表示和操做HTML和XML文檔內容的基礎API。react

《Javascript DOM編程藝術(第二版)》裏面將對定義的解釋展開成三部分裏來講。也便是說:web

  • D-document

若是沒有document(文檔),DOM也就無從談起。當建立了一個網頁並將它加載到web瀏覽器中時,DOM就在幕後悄然而生。它把你編寫的網頁文檔轉換爲一個文檔對象。編程

  • O-object

在程序設計語言中,「對象」這個詞的含義是很是明確的。「對象」是一種自給自足的數據集合。與某個特定的對象相關聯的變量被稱爲這個對象的屬性;只能經過某個特定對象去跳用的函數被稱之爲這個對象的方法。swift

  • M-model

DOM把一份文檔表示爲一顆樹......更具體說,DOM把文檔表示爲一顆家譜樹。家譜樹自己又是一種模型。與使用「家譜樹」這個術語相比,把文檔稱爲「節點樹」更準確。數組

MDN web docs裏面是這樣說的:

The Document Object Model (DOM) is a programming interface for HTML and XML documents. It represents the page so that programs can change the document structure, style, and content. The DOM represents the document as nodes and objects. That way, programming languages can connect to the page.

根據上面的定義,結合我本身的理解,咱們能夠給DOM下這樣的一個定義:

DOM是把文檔(通常包括HTML文檔和XML文檔)轉換爲樹型可編程文檔對象的一種接口。

在這裏,咱們也能夠把這種接口理解爲規範。由於當這種接口被衆多瀏覽器所實現的時候,它無疑表明着一種行業規範。咱們也能夠把這種接口理解爲一種從真實文檔到可編程對象的一種映射關係。而最終完成這種映射的操做是由瀏覽器內核來完成的。而現代瀏覽器支持的腳本語言javascript。因此,若是你以爲上面咱們的定義實在太抽象的話,那麼咱們能夠簡單地理解爲:

DOM是一種關於如何在HTML標籤javascript對象之間創建一一對應關係的規範。

爲何說是簡單理解呢?由於標籤不必定是HTML標籤,還能夠是XML標籤。對象也不必定是javascript的,也能夠是python的,以下:

# Python DOM example
import xml.dom.minidom as m
doc = m.parse(r"C:\Projects\Py\chap1.xml")
doc.nodeName # DOM property of document object
p_list = doc.getElementsByTagName("para")

複製代碼

上面所說的javascript對象在語義上說就是《Javascript DOM編程藝術(第二版)》中所提到的對象,也就是說傳統面向對象編程語言所說的「對象」-有本身特定的屬性和方法。這跟廣義上「javascript對象」的概念仍是有區別的。更準確點,咱們稱之爲「原生DOM對象」。

假如,咱們有如下HTML文檔:

<html>
  <head>
    <title>DOM Tests</title>
  </head> 
  <body>
    <div id="test"></div>
  </body>
</html>
複製代碼

那麼HTML標籤<div id="test"></div>所對應的原生DOM對象就能夠經過document.getElementById方法來得到。該原生DOM對象有本身的屬性和方法:

let divElement = document.getElementById('test')
let textNode = document.createTextNode('我是文本節點')

// 是個javascript對象
console.log(typeof divElement) // "object"

// 有本身的屬性,好比id
console.log(divElement.id) // "test"

/* 有本身的方法,好比appendChild()
 * 最終在文檔中的表現爲:<div id="test">我是文本節點</div>
*/ 
divElement.appendChild(textNode) 
複製代碼

好,弄清楚什麼是「(real)DOM」以後,那麼「virtual DOM」就比較好理解了。顧名思義,virtual DOM無非指的就是非真正的DOM,是虛擬的。虛擬,虛擬,那擬的是誰的呢?固然是「(real)DOM」啦。在DOM的世界裏面,它們是用一個原生DOM對象來描述文檔樹的一個節點的。那麼與此類比,virtual DOM的世界咱們用一個pure javascript object(也就是咱們平時所說的字面量對象,有點想swift的hash table)來描述一個文檔節點,好像也說得通。咱們說幹就幹。

// 字面量對象
 let virtualDOM ={}
複製代碼

原生DOM對象有nodeName屬性,表明着文檔元素的標籤名稱,咱們也得有。爲了區別於原生,咱們取名爲「type」。

// 字面量對象
 let virtualDOM ={
   type:string
 }
複製代碼

文檔元素通常都會有衆多屬性,對應到原生DOM對象中,它也有一個叫attributes的屬性。仍是爲了區別與原生,咱們取名爲「properties」,簡寫爲「props」。不過相比DOM裏面的「元素屬性集合也是一個節點集合(nodeList)」,咱們這裏簡化一下,也就是說仍是用key-value組成的字面量對象來表示屬性集合。

// 字面量對象
 let virtualDOM ={
   type:string,
   props:{
    prop1:value1,
    prop2:value2,
    ......
   }
 }
複製代碼

由於文檔具備自然的樹狀結構,因此一個元素與另外的一個元素一定存在這樣那樣的關係。要麼A是B的父節點,要麼C是B的兄弟節點。在DOM中,咱們正是經過這種關係來遍歷整個文檔樹。仍是拿上面的divElement元素舉例。

其實,在文檔樹中,一個元素跟另一個元素的關係無非就兩種:父子關係和兄弟關係:

divElement.parentNode  -> <body />
divElement.previousSibling -> null // divElement並無往下的兄弟節點
divElement.nextSibling -> null  // divElement並無往下的兄弟節點
divElement.childNodes  -> ['我是文本節點']
複製代碼

那麼對比原生DOM對象,在virtual DOM對象上,咱們該如何設計出對應的數據結構來體現這兩種關係呢?答案是多樣化的。你能夠這樣設計:

// 字面量對象
 let virtualDOM ={
   type:string,
   props:{
    prop1:value1,
    prop2:value2,
    ......,
    children:[child1,child2,......]
   }
 }
複製代碼

上面的這種設計,就是讓children也成爲了一個屬性,只不過使用的時候,不能把它看成一個真正的屬性來用。

你也能夠這樣設計:

// 字面量對象
 let virtualDOM ={
   type:string,
   props:{
    prop1:value1,
    prop2:value2,
    ......
   },
   children:[
        child1,
        child2,
        ......
    ]
 }
複製代碼

這種設計相比前者,把children屬性放在與props屬性平級的層級上,我的感受從結構角度上來評價,後者更好理解。可是不知道爲啥,react官方採用了前者的設計方案。不管哪一種設計方案,都是在virtual DOM節點中保存一個指向它的全部子節點的引用來體現父子關係的。而數組中相鄰的兩項則是兄弟關係的體現。

至此,咱們經過理解DOM,並運用依樣畫葫蘆的手法,咱們完成了virtual DOM對象的數據結構的設計。這是十分基礎且重要的一步。若是說,現實世界是由原子堆疊而成的話,那麼咱們也能夠說,文檔就是由節點(node,node又分爲elecment node,text node和 attribute node)堆疊而成,DOM就是由原生DOM對象堆疊而成,virtual DOM就是由virtual DOM對象堆疊而成的。在react的世界裏面,virtual DOM對象是構建應用的基本單元。咱們正是經過把[對virtual DOM的更新]映射[爲對real DOM的更新],再經過瀏覽器把[對real DOM的更新]映射爲[對文檔的更新]來完成界面的更新的。

下一篇文章,我就來談談如何使用javascript來實現這種「映射」。 更具體地說,就是如何將

{
  type:'div',
  props:{
    id:'test',
    children:['我是文本節點']
  }
}
複製代碼

最終「映射」爲真正文檔上的一個元素節點:

<div id="test">我是文本節點</div>
複製代碼

下篇:按部就班DIY一個react(二)

相關文章
相關標籤/搜索