前沿技术解密——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
相关文章
相关标签/搜索