Vue的虚拟DOM

DOM的解析流程分为五步
创建DOM树——创建StyleRules——创建Render树——布局Layout——绘制Painting

  • 处理HTML标签建立DOM树
  • 处理CSS标签建立CSSOM树
  • 连接CSSOM树和DOM树形成一个render树
  • 在render树上运行布局来计算每个节点的形状
  • 在屏幕上画每一个节点

vdom的解析流程

  • 用JavaScript对象模拟DOM
  • 把此虚拟DOM转成真实DOM并插入页面中
  • 如果有事件发生修改了虚拟DOM
  • 比较两棵虚拟DOM树的差异,得到差异对象
  • 把差异对象应用到真正的DOM树上

虚拟DOM与传统DOM相比的优势

对于传统DOM,如果一个操作需要修改5次DOM,那么浏览器就会依次完成5次修改,修改DOM的代价很大,这样会做很多无用操作,浪费性能。
而对于虚拟DOM,同样的一个操作,并不会马上修改真实的DOM,而是将每一次修改生成的虚拟DOM进行diff比较并将不同的地方保存在JS对象中(虚拟DOM),在5次操作结束后再一起映射到真实DOM中去,从而避免无用的计算。

关于diff算法

当数据发生改变时,set方法会让调用Dep.notify通知所有订阅者Watcher,订阅者就会调用patch给真实的DOM打补丁,更新相应的视图。

  • diff算法只会对同层次节点进行同比较,这是为了降低算法的复杂度,提高效率。
  • 首先判断两个节点是否值得比较(不看子节点是否相同),如果节点不同则直接替换,如果相同则用patch经行比较
patchVnode (oldVnode, vnode) {
    const el = vnode.el = oldVnode.el
    let i, oldCh = oldVnode.children, ch = vnode.children
    if (oldVnode === vnode) return
    if (oldVnode.text !== null && vnode.text !== null && oldVnode.text !== vnode.text) {
        api.setTextContent(el, vnode.text)
    }else {
        updateEle(el, vnode, oldVnode)
        if (oldCh && ch && oldCh !== ch) {
            updateChildren(el, oldCh, ch)
        }else if (ch){
            createEle(vnode) //create el's children dom
        }else if (oldCh){
            api.removeChildren(el)
        }
    }
}

找到对应的真实dom,称为el
判断Vnode和oldVnode是否指向同一个对象,如果是,那么直接return
如果他们都有文本节点并且不相等,那么将el的文本节点设置为Vnode的文本节点。
如果oldVnode有子节点而Vnode没有,则删除el的子节点
如果oldVnode没有子节点而Vnode有,则将Vnode的子节点真实化之后添加到el
如果两者都有子节点,则执行updateChildren函数比较子节点,这一步很重要

下面是updateChildren过程

  • Vue中diff实现了oldStart+oldEnd,newStart+newEnd俩对指针,分别对应oldVdom和newVdom的起点和终点。起止点之前的节点是待处理的节点,Vue不断对vnode进行处理同时移动指针直到其中任意一对起点和终点相遇。处理过的节点Vue会在oldVdom和newVdom中同时将它标记为已处理。

  • 逐个遍历newVdom的节点,找到它在oldVdom中的位置,如果找到了就移动对应的DOM元素,如果没找到说明是新增节点,则新建一个节点插入。遍历完成之后如果oldVdom中还有没处理过的节点,则说明这些节点在newVdom中被删除了,删除它们即可。如果newVdom中还有没处理的节点,就说明新增了节点。

  • 这样层层递归下去,直到将oldVnode和Vnode中的所有子节点比对完。

你可能感兴趣的:(vue)