【轉】Virtual DOM

前言

React 好像已經火了好久好久,以至於咱們對於 Virtual DOM 這個詞都已經很熟悉了,網上也有很是多的介紹 React、Virtual DOM 的文章。可是直到前不久我專門花時間去學習 Virtual DOM,才讓我對 Virtual DOM 有了必定的理解,以至於要懷疑起好久以前看過的那些文章來。倒不是這些文章講得不對,而是如今在我看來角度不太好,說得越多,越說不清。javascript

讓我可以有所開竅(自認爲)的,是這篇文章:html


Change And Its Detection In JavaScript Frameworks
Monday Mar 2, 2015 by Tero Parviainen
http://teropa.info/blog/2015/03/02/change-and-its-detection-in-javascript-frameworks.html前端


做者看問題的角度很棒,從數據變動與UI同步的角度來介紹各個典型框架,特別是對於 React 的 Virtual DOM,從這個角度理解起來更容易些。java

感興趣的同窗,若是沒有讀過這篇文章,推薦去看一看,不感興趣就算了。不過接下來我要講的東西,部分整理自這篇文章,特別是從這篇文章中引用的圖片,很是棒。固然還有我本身的一些思考,以及一些對於目前 Virtual DOM 實現的開源庫的分析。node

若是讀了上面推薦的這篇文章,我卻是不介意你再也不繼續把本文讀下去,由於有些東西你已經領會到了。固然,也不反對。git

變化這件事

談論頁面的變化以前,我們先看下數據和頁面(視覺層面的頁面)的關係。數據是隱藏在頁面底下,經過渲染展現給用戶。一樣的數據,按照不一樣的頁面設計和實現,會以不一樣形式、樣式的頁面呈現出來。有時候在一個頁面內的不一樣位置,也會有相同數據的不一樣表現。github

 
Paste_Image.png

Web 的早期,這些頁面一般是靜態的,頁面內容不會變化。而若是數據發生了變化,一般須要從新請求頁面,獲得基於新的數據渲染出的新的頁面。數組

 
Paste_Image.png

至少,這個模式理解起來挺簡單不是嗎。瀏覽器

直到 Web 應用複雜起來,開發者們開始關注用戶體驗,開始將大量的處理向前端遷移,頁面變得動態、靈活起來。一個顯著的特徵是,數據發生變化以後,再也不須要刷新頁面就能看到頁面上的內容隨之更新了。性能優化

前端須要作的事情變得多了起來,前端工程師們也就修煉了起來,各類前端技術也就出現了。

首先,聰明的工程師們發現既然是在前端渲染頁面,若是隻是部分數據發生了變化,就要把頁面總體或一大塊區域從新渲染就有點笨了。爲何不把事情作得更極致些,只更新變化的數據對應的頁面的內容呢?

怎麼作呢?操做 DOM 唄。DOM 就是瀏覽器提供給開發者用於操做頁面的模型嘛,直接經過腳原本調用 DOM 的各類接口就 OK 了。並且咱們還有了像 jQuery 這樣的棒棒的工具,操做 DOM 變得 so easy。

然而,頁面愈來愈複雜,聰明的工程師們發現數據變化以後,總是須要手動編碼去操做對應的 DOM 節點執行更新,有點煩,不夠懶啊。因而各類框架如雨後春筍般出現了,紛紛表示能夠簡化這個過程。

稍微早期的框架有這樣的:

 
Paste_Image.png

開發者藉助框架,監聽數據的變動,在數據變動後更新對應的 DOM 節點。雖然仍是要寫一些代碼,可是寫出來的代碼好像頗有條理的樣子,至少更容易理解和維護了,也不錯嘛。

更進一步,MVVM 框架出現了,以 AngularJS 爲表明:

 
Paste_Image.png

仍然是數據變化後更新對應 DOM 節點的方式,可是創建這種綁定關係的過程被框架所處理,開發者要寫的代碼變少了,並且代碼更易讀和維護了。

再而後呢,你們就在這個棒棒的模式上繼續深耕,紛紛表示還能夠在性能上作得更好,前端領域一片繁榮。

再後來 React 出現了,它不只不是 MVVM 框架,甚至連 MV* 框架都不是。這年頭,不是個 MV* 框架還好意思出門?可 React 還真的帶來了新的思路!

什麼思路呢?

就是回到過去,回到那個簡單而美好的時候。具體而言,就是每次數據發生變化,就從新執行一次總體渲染。的確這樣更簡單,不用去琢磨究竟是數據的哪一部分變化了,須要更新頁面的哪一部分。可是壞處太明顯,體驗很差啊。而 React 給出瞭解決方案,就是 Virtual DOM。

Virtual DOM 概況來說,就是在數據和真實 DOM 之間創建了一層緩衝。對於開發者而言,數據變化了就調用 React 的渲染方法,而 React 並非直接獲得新的 DOM 進行替換,而是先生成 Virtual DOM,與上一次渲染獲得的 Virtual DOM 進行比對,在渲染獲得的 Virtual DOM 上發現變化,而後將變化的地方更新到真實 DOM 上。

簡單來講,React 在提供給開發者簡單的開發模式的狀況下,藉助 Virtual DOM 實現了性能上的優化,以至於敢說本身「不慢」。

Virtual DOM

React 基於 Virtual DOM 的數據更新與UI同步機制:

 
React - 初始渲染

初始渲染時,首先將數據渲染爲 Virtual DOM,而後由 Virtual DOM 生成 DOM。

 
React - 數據更新

數據更新時,渲染獲得新的 Virtual DOM,與上一次獲得的 Virtual DOM 進行 diff,獲得全部須要在 DOM 上進行的變動,而後在 patch 過程當中應用到 DOM 上實現UI的同步更新。

Virtual DOM 做爲數據結構,須要能準確地轉換爲真實 DOM,而且方便進行對比。除了 Virtual DOM 外,React 還實現了其餘的特性,爲了專一於 Virtual DOM,我另外找了兩個比較 Virtual DOM 來學習:

這裏也推薦給感興趣且尚未讀過兩個庫源碼的同窗。

因爲只關注 Virtual DOM,經過閱讀兩個庫的源碼,對於 Virtual DOM 的定位有了更深一步的理解。

首先看數據結構。

** Virtual DOM 數據結構 **

DOM 一般被視爲一棵樹,元素則是這棵樹上的節點(node),而 Virtual DOM 的基礎,就是 Virtual Node 了。

在 virtual-dom 中,給 Virtual Node 聲明瞭對應的類 VirtualNode,基本是用於存儲數據,包括:

  • tagName
  • properties
  • children
  • key
  • namespace
  • count
  • hasWidgets
  • hasThunks
  • hooks
  • descendantHooks

Snabbdom 的 Virtual Node 則是純數據對象,經過 vnode 模塊來建立,對象屬性包括:

  • sel
  • data
  • children
  • text
  • elm
  • key

雖然有所差異,除去實現上的差異和庫自己的額外特性,能夠看到 Virtual Node 用於建立真實節點的數據包括:

  • 元素類型
  • 元素屬性
  • 元素的子節點

有了這些其實就能夠建立對應的真實節點了。

建立 Virtual DOM

嵌套 Virtual Node 就能夠獲得一棵樹了。virtual-dom 和 Snabbdom 都提供了函數調用的方式來建立 Virtual Tree,這個過程就是渲染了:

var vTree = h('div', [ h('span', 'hello'), h('span', 'world') ]) 

React 提供 JSX 這顆糖,使得咱們能夠用相似 HTML 的語法來編寫,不過編譯後實質仍是經過函數調用來獲得一棵嵌套的 Virtual Tree。並且這對於理解 Virtual DOM 機制來講不是特別重要,先無論這個。

使用 Virtual DOM

首先來看初始化,virtual-dom 提供了 createElement 函數:

var rootNode = createElement(tree) document.body.appendChild(rootNode) 

根據 Virtual Node 建立真實 DOM 元素,而後再追加到頁面上。

再來看更新。virtual-dom 有明確的兩步操做,首先 diff,而後 patch:

var newTree = render(count) var patches = diff(tree, newTree) rootNode = patch(rootNode, patches) 

而 Snabbdom 則簡單些,只有一個 patch 函數,內部在進行比對的同時將更新應用到了真實 DOM 上,並且初始化也是用的 patch 函數:

var vnode = render(data) var container = document.getElementById('container') patch(container, vnode) // after data changed var newVnode = render(data) patch(vnode, newVnode) 

性能優化

關於性能優化,除了 Virtual DOM 機制自己提供的特性之外,再就是不一樣的 Virtual DOM 庫自身的優化方案了,這個能夠看上面兩個庫的文檔,再也不贅述。

其實提到 Virtual DOM 的差別比對,有人會對其內部如何處理數組感興趣。的確,若是數組元素的位置發生了改變,這個要識別起來是有點麻煩。爲此,上面兩個庫和 React 其實都在 Virtual Node 上額外記錄了一個屬性「key」,就是用來輔助進行 Virtual Node 的比對的。

簡單來講,若是兩個 Virtual Node 的位置不一樣,可是 key 屬性相同,那麼會將這兩個節點視爲由相同數據渲染獲得的,而後進一步進行差別分析。因此,並非僅僅按照位置進行比對,具體的實現能夠查看各個庫的源碼。

小結

OK,以上就是我要講的所有全部內容了。

相信不少同窗以前對 Virtual DOM 已經很熟悉了,比我理解得更深刻的同窗相信也不會少。不過從「數據變化與UI同步更新」這個角度來理解 Virtual DOM,在我看來是比較好的,因此整理在這裏了。

有個問題挺常見,AngularJS 和 React 哪一個更好?

若是說各有千秋的話,估計你們就「呵呵」了。可是這兩個框架/庫從「數據變化與UI同步更新」的角度來看,的確都解決了問題,並且解決問題的方式你們都挺承認(至少在喜歡它們的同窗眼裏是這樣的)。

並且,若是你們關注 Vue 的話,能夠看到,這個 MVVM 框架已經發布了 2.0,其中就採用了 Virtual DOM 實現其UI同步更新!因此,這的確不矛盾啊。

第二個並且,技術自己不是目的,可以更好地解決問題纔是王道嘛。

做者:luobo_tang連接:https://www.jianshu.com/p/bef1c1ee5a0e

相關文章
相關標籤/搜索