做爲
React
的核心技術之一Virtual DOM
,一直披着神祕的面紗。node
實際上,Virtual DOM包含:react
- Javascript DOM模型樹(VTree),相似文檔節點樹(DOM)
- DOM模型樹轉節點樹方法(VTree -> DOM)
- 兩個DOM模型樹的差別算法(diff(VTree, VTree) -> PatchObject)
- 根據差別操做節點方法(patch(DOMNode, PatchObject) -> DOMNode)
接下來咱們分別探討這幾個部分:git
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(' ')]) ]);
這方法也不太難,咱們實現一個簡單的:算法
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; }
差別算法是Virtual DOM的核心,實際上該差別算法是個取巧算法(固然你不能期望用O(n^3)的複雜度來解決兩個樹的差別問題吧),不過能解決Web的大部分問題。數組
那麼React是如何取巧的呢?markdown
如圖,React僅僅對同一層的節點嘗試匹配,由於實際上,Web中不太可能把一個Component在不一樣層中移動。app
還記得以前在VTree中的屬性有一個叫key的東東麼?這個是一個VNode的惟一識別,用於對兩個不一樣的VTree中的VNode作匹配的。dom
這也很好理解,由於咱們常常會在Web遇到擁有惟一識別的Component(例如課程卡片、用戶卡片等等)的不一樣排列問題。
React提供自定義元素,因此匹配更加簡單。
因爲diff操做已經找出兩個VTree不一樣的地方,只要根據計算出來的結果,咱們就能夠對DOM的進行差別渲染。
具體可參考下面兩份代碼實現: