JavaScript進階(三十):理解虛擬 DOM

這裏我們就來說一說虛擬 DOM 這個東西,首先我們必須得先把它的概念弄明白了,那麼先說下我是怎麼理解虛擬 DOM 的。

虛擬 DOM 它跟我們別的東西不太一樣,比方說,我們在 js 裏面寫一個類,不用想,就是 class,只要你用的是 ES6,全天下就這一個寫法。

再比方說,我想給某個東西加樣式,那我如果用 id 來獲取元素,最多就是 getElementById,或者 querySelector,就這麼幾種方法,因爲這些都是一個標準化的東西。

 

虛擬 DOM,其實並不是一個標準,什麼意思呢?

我們平常用的那些標準,都是 W3C 搞出來的,而虛擬 DOM 到目前爲止,更多的,其實還是大家自己搞出來的一個東西。並且虛擬 DOM 這個東西,說句特別實在的話,它在很長的一段時間之間,估計都不會成爲 W3C 的一個標準。

 

那爲什麼我有這個把握呢?原因很簡單,因爲假設它成爲標準了,其實是沒有意義的,爲什麼?

因爲我們爲什麼要用虛擬 DOM,而不用普通的 DOM?就是因爲普通的 DOM 性能太差了。

那如果從官方的角度考慮,與其說在另搞一套東西出來,還不如把原來那個優化好了,所以說在我看來,虛擬 DOM 估計很長的一段時間,應該不會成爲標準的一部分。

 

所以說白了,虛擬 DOM 沒有標準可言,我寫虛擬 DOM,可能喜歡這樣寫,他寫可能是那樣的,誰都能寫出個不一樣的感覺來。

當然,好在現在,能夠自己獨立編寫一整套虛擬 DOM 庫的人,相對來說還比較少,所以並沒有導致一個特別大的混亂。

所以這東西沒什麼寫的標不標準,我想怎麼寫,就怎麼寫,只要性能上靠譜,用起來方便,它就是對的。

 

在我們前面的博客裏面,一直有提到虛擬 DOM,那麼這裏我們就詳細的來講講,首先,當初這個虛擬 DOM 爲什麼而產生?

我們知道真實的 DOM,其實它的特點就是慢,特別的慢。它的操作性能,比普通操作要慢好幾千倍,在我這臺電腦上,一般測出來的是 3000 - 4000 倍左右。

所以真實的 DOM 非常慢,那麼這個時候,我們一定要做到一點,就是說之所以虛擬 DOM 能夠產生作用,原因就在於很簡單的一點,你要儘量的,減少去使用真實的 DOM。

說白了,就是你就只進行最少限度的真實 DOM 操作,儘可能的讓虛擬 DOM 來承擔你的東西。

 

然後,虛擬 DOM 這個東西,其實你不要把它想的太複雜了,好厲害的感覺,完全不是,它就跟我們平常用的對象差不多。

說白了,它就是一個普通的對象,只不過,它上面是精確的去複製了,我們真實 DOM 它應該有的東西。

最後,我們還關心一個事,就是虛擬 DOM 它所產生的任何變化,我們需要想辦法去給它同步回真實的 DOM,就是更新 UI,頁面也得跟着變。

 

然後接下來,我們就直接來試試,看看虛擬 DOM 具體怎麼做。

首先,我們先不管別的,別一上來,就追求完美,規劃的特別好等等,不存在的,這個世界上就沒人能做到,一開始的時候面面俱到,想的特清楚,後來完全不用改,這是扯淡。

所以現在這時候,我們先別管,也不用想,就先用最笨的方法,能出來東西就行,然後後面在逐漸去改進它,實在太亂寫不下去了,我們還可以去重構。

 

那麼現在我們就來一個例子,比方說,我這裏面有個 div,它上面有一些屬性:

然後它裏面還有一些東西:

那麼現在這個時候,真實的 DOM 節點有幾個?

div,h2,ul,3 個 li 一共 6 個,並且這 6 個真實的節點之間,它們還是有一定的層級關係。

這些都是我們需要去仿照出來的,虛擬 DOM 的結構,一定是跟真實 DOM 保持一致的。

而且,這裏面有可能涉及到各種不同的節點,比如我在中間加了一堆文字:

當然,99% 的情況下,我們一般只關心 2 種節點。

一種是元素,而另一種就是文本節點,因爲這些文本里面,它裏面可能包含一些模板之類的東西,比如 {{ }}。

所以,我們主要關心元素和文本這兩種節點。

 

那麼既然它這有兩種節點,說的實在一點,我希望我們虛擬 DOM 那邊的虛擬節點,也是兩種,爲什麼?因爲這樣我處理起來簡單一些。

在我那個用來模擬元素的節點當中,我只需要管好跟元素相關的事,別的我不管。

然後跟文本相關的那個節點當中,我就直接管好文本相關的事就行,別的也跟我沒關係。

 

所以,既然我們現在有了這樣的一個想法之後,就可以做一個事。

首先,這兩種節點,你別看它有一些區別和不同,但是,它們其實還是有一樣的地方。

比方說,元素和文本是不是都有父級?我能不能去更新一個文本節點的內容?我能不能去更新一個元素上面的東西,比如 title 變了。

所以說,在這種情況下,你會發現,它們之間還有一些共通的東西。

那麼這個時候,我們就可以畫一個草圖,虛擬元素和虛擬文本這兩個東西,它們之間,絕對不是相互之間毫無關聯的,而是有很多共通的東西。

所以就我個人而言,我更希望它是一個繼承的關係:

VNode 是父級,假設我將來想加個別的什麼節點,我也可以從它裏面來繼承。

VElement,虛擬元素節點。

這裏順便一說,有人可能會想,你這個 VElement 不太合理,因爲 Element 它畢竟是一個很通用很廣義的東西,它指的是所有的元素,那我們有沒有可能,在搞出一個 VDiv,VSpan 之類的東西呢?

其實有的框架是這麼做的,比如 react,它裏面就是所有的 HTML 元素,它都有對應的抽象。這樣做的好處,就是它每一個獨立出來的那個類,都可以更好的處理這個節點。

因爲說白了,你比方說像 a,它是不是有一些特殊的東西,比方說 a 有 href,你能找出第二個有 href 的東西來嗎?所以這個 href 並不是所有人都有,那這時候,如果我用一個 AElement 來處理,是不是更加的方便?

當然,我們這裏具體就知道有這麼個事就行,現在我們也不會去抽象那麼多級,不然太麻煩了,就用一個 VElement,來代表所有。

VText,虛擬文本節點。

VElement 和 VText 這兩種節點,都是以 VNode 作爲父級來繼承出來的,這樣的話,很多相關的東西,我們都可以放到 VNode 裏面,所以按照這種說法, 我們就可以搞一個公共的父級,然後它們兩個分別來繼承,這是最合理的。

 

那麼首先,我們這有幾個東西。

首先是 VNode.js,廣義上的,泛指所有節點。

元素,註釋,文本這些都是節點。

然後接下來,我們肯定還需要兩個東西。

一個是 VElement.js,它的作用,就是專門的用來描述一個元素。

當然,它不是憑空從石頭縫裏面蹦出來的,它是從父級繼承來的:

以及,我們還需要一個節點 VText,它是一個虛擬的文本節點,我們以後對文本的各種更新,都是從它上面走:

最後,我們還需要引入它們:

那麼現在我們就已經抽出來這三種東西。

下篇博客,我們就要來考慮考慮,它們都分別有什麼。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章