虚拟元素节点VNode
什么是虚拟元素节点?
虚拟元素节点即对真实dom节点的描述。包含标签名、标签属性描述对象、子节点集合。
// example
{
    tag:'div'
    props:{
        key:'uuid',//VNode唯一key,新旧VNode diff时有用
        id:'div',//VNode id值
        //...
    }
}
virtual dom优势?
不用直接操作dom,虚拟DOM具有批处理和高效的Diff算法,可以减少重排与重绘,提高性能;virtual dom是javascript对象,具有跨平台优势;
virtual dom diff思路
  • VNode变化类型:
REPLACE:当前节点节点替换。比较方式:同一位置新旧节点一一对比。结构:{type:0,node:newNode}。
REORDER:子节点集合中新增、删除的子节点。比较方式:子节点集合同级对比。结构:{type:1,moves:[{type:0——删除,index:该子节点在子节点集合中的下标},{type:1——新增,index:该子节点在子节点集合中的下标,item:newVNode}]}。
PROPS:当前节点属性变化。比较方式:同一位置新旧节点一一对比。结构:{type:2,props:[{修改propKey:undefined——删除该属性,propKey:newPropValue}]}。
TEXT:当前节点文本节点变化。比较方式:同一位置新旧节点一一对比。结构:{type:3,content:newText}。
  • 新旧虚拟dom diff思路总结:由于跨级修改很少,可忽略不计,所以仅进行同级子节点集合对比。
    注意:新旧虚拟dom diff对比时,采用递归方法,并且从上往下统计。

    • 对比结果存放patches[key]:key为virtual dom tree递归时节点所处的位置,便于在patch应用在actual dom tree时,能准确找到对应的节点。
    • 先进行newVNodeList与oldVNodeList中同位置(同index)的VNode对比,统计VNode的props、text,VNode被替换(标签名或VNode唯一key不一致)的改变
    • 再进行同级childVNodeList对比,统计子节点删除、新增情况。并且返回含oldVNodeList.length长度和newVNodeList中VNode/Text值的新数组。通过VNode唯一key进行对比统计。
对比结果patches,应用到真实dom

注意:将对比结果patches,应用到真实dom时,采用递归方法,并且从下往上应用。

  • patch.type===0(替换节点):node.parentNode.replaceChild(newNode,oldNode)
  • patch.type===1(删除、新增子节点): removeChild(removeNode)、cloneNode(true)、insertBefore(insertNode,beforeNode)
  • patch.type===2(props变化):removeAttribute(prop)、setAttribute(prop)
  • patch.type===3(text变化):elementNode.textContent、textNode.nodeValue

参考网站:
深度剖析:如何实现一个 Virtual DOM 算法:https://github.com/livoras/blog/issues/13
同级VNodeList第一次对比代码:https://github.com/livoras/list-diff/blob/master/lib/diff.js


qzuser
31 声望4 粉丝