做为 React 的关键技术众所周知 Virtual DOM,始终披著谜样的盖头。
JavaScript DOM 数学模型树(VTree),类似于文件格式结点树(DOM)DOM 数学模型树转结点树方式(VTree -> DOM)三个 DOM 数学模型树的差别演算法(diff(VTree, VTree) -> PatchObject)依照差别操作方式结点方式(patch(DOMNode, PatchObject) -> DOMNode)
VTree
VTree 数学模型比较单纯,基本上内部结构如下表所示:
{ // tag的英文名字 tagName: p, // 结点包涵属性 properties: { style: { color: #fff } }, // 子结点 children: [], // 该结点的惟一则表示,前面会讲G540用 key: 1}
因此他们很难写三个方式来建立此种抽象化内部结构,比如 React 是那么建立的:
// 建立三个divreact.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 仅仅对同一层的结点尝试匹配,因为事实上,Web 中不太可能把三个 Component 在不同层中移动。
基于 key 来匹配
还记得之前在 VTree 中的属性有三个叫 key 的东东么?这个是三个 VNode 的惟一识别,用于对三个不同的 VTree 中的 VNode 做匹配的。
这也很好理解,因为他们经常会在 Web 遇到拥有惟一识别的 Component(比如课程卡片、用户卡片等等)的不同排列问题。
基于自定义元素做优化
patch(DOMNode, PatchObject) -> DOMNode
由于 diff 操作方式已经找出三个 VTree 不同的地方,只要依照计算出来的结果,他们就可以对 DOM 的进行差别渲染。
扩展阅读
@Matt-Esch同时实现的:virtual-dom他们自己做的简版同时实现,用于Mobile页面渲染的:qvd