前沿技術解密——VirtualDOM

 

做爲React的核心技術之一Virtual DOM,一直披着神祕的面紗。node

實際上,Virtual DOM包含:react

  1. Javascript DOM模型樹(VTree),相似文檔節點樹(DOM)
  2. DOM模型樹轉節點樹方法(VTree -> DOM)
  3. 兩個DOM模型樹的差別算法(diff(VTree, VTree) -> PatchObject)
  4. 根據差別操做節點方法(patch(DOMNode, PatchObject) -> DOMNode)

接下來咱們分別探討這幾個部分:git

VTree

VTree模型很是簡單,基本結構以下:github

{
    // tag的名字 tagName: 'p', // 節點包含屬性 properties: { style: { color: '#fff' } }, // 子節點 children: [], // 該節點的惟一表示,後面會講有啥用 key: 1 } 

因此咱們很容易寫一個方法來建立這種樹狀結構,例如React是這麼建立的:web

// 建立一個div react.createElement('div', null, [ // 子節點img react.createElement('img', { src: "avatar.png", class: "profile" }), // 子節點h3 react.createElement('h3', null, [[user.firstName, user.lastName].join(' ')]) ]); 

VTree -> DOM

這方法也不太難,咱們實現一個簡單的:算法

function create(vds, parent) { // 首先看看是否是數組,若是不是數組統一成數組 !Array.isArray(vds) && (vds = [vds]); // 若是沒有父元素則建立個fragment來當父元素 parent = parent || document.createDocumentFragment(); var node; // 遍歷全部VNode vds.forEach(function (vd) { // 若是VNode是文字節點 if (isText(vd)) { // 建立文字節點 node = document.createTextNode(vd.text); // 不然是元素 } else { // 建立元素 node = document.createElement(vd.tag); } // 將元素塞入父容器 parent.appendChild(node); // 看看有沒有子VNode,有孩子則處理孩子VNode vd.children && vd.children.length && create(vd.children, node); // 看看有沒有屬性,有則處理屬性 vd.properties && setProps({ style: {} }, vd.properties, node); }); return parent; } 

diff(VTree, VTree) -> PatchObject

差別算法是Virtual DOM的核心,實際上該差別算法是個取巧算法(固然你不能期望用O(n^3)的複雜度來解決兩個樹的差別問題吧),不過能解決Web的大部分問題。數組

那麼React是如何取巧的呢?markdown

  1. 分層對比

如圖,React僅僅對同一層的節點嘗試匹配,由於實際上,Web中不太可能把一個Component在不一樣層中移動。app

  1. 基於key來匹配

還記得以前在VTree中的屬性有一個叫key的東東麼?這個是一個VNode的惟一識別,用於對兩個不一樣的VTree中的VNode作匹配的。dom

這也很好理解,由於咱們常常會在Web遇到擁有惟一識別的Component(例如課程卡片、用戶卡片等等)的不一樣排列問題。

  1. 基於自定義元素作優化

React提供自定義元素,因此匹配更加簡單。

patch(DOMNode, PatchObject) -> DOMNode

因爲diff操做已經找出兩個VTree不一樣的地方,只要根據計算出來的結果,咱們就能夠對DOM的進行差別渲染。

擴展閱讀

具體可參考下面兩份代碼實現:

  1. @Matt-Esch實現的:virtual-dom
  2. 咱們本身作的簡版實現,用於Mobile頁面渲染的:qvd
相關文章
相關標籤/搜索