虚拟DOM

155 阅读1分钟

template转换成视图的过程

  1. Vue.js通过编译将template模板转换成渲染函数(render),执行渲染函数就可以得到一个虚拟节点树。
  2. 对 Model 进行操作时,会触发对应 Dep 中的 Watcher 对象。Watcher 对象会调用对应的 update 来修改视图。这个过程主要是将新旧虚拟节点进行差异对比,即patch(核心是diff算法),然后根据对比结果进行DOM操作来更新视图。

原理:

例子:

虚拟DOM

虚拟DOM是一个JS对象(VNode节点),至少包含三个属性:标签名(tag)、属性(attrs)和子元素对象( children)。
Vue的diff算法是基于snabbdom改造过来的,仅在同级的vnode间做diff,递归地进行同级vnode的diff,最终实现整个DOM树的更新。因为跨层级的操作是非常少的,忽略不计,这样时间复杂度就从O(n³)变成O(n)。

diff 算法:

  1. patch(container,vnode)
    初次渲染时,让VNode渲染成真正的DOM。
function createElement(vnode) {    
var tag = vnode.tag  
var attrs = vnode.attrs || {}    
var children = vnode.children || []    
if (!tag) {       
 return null  
}    
// 创建真实的 DOM 元素    
var elem = document.createElement(tag)   
// 属性    
var attrName    
for (attrName in attrs) {    
    if (attrs.hasOwnProperty(attrName)) { 
           // 给 elem 添加属性
           elem.setAttribute(attrName, attrs[attrName])
        }
    }
    // 子元素
    children.forEach(function (childVnode) {
        // 给 elem 添加子元素,如果还有子节点,则递归的生成子节点。
        elem.appendChild(createElement(childVnode))  // 递归
    })
 // 返回真实的 DOM 元素   
 return elem
}
  1. patch(vnode,newVnode)
    再次渲染时,将新的vnode和旧的vnode相对比,然后之间差异应用到所构建的真正的DOM树上。 这里我们只考虑vnode与newVnode如何对比的情况:
function updateChildren(vnode, newVnode) {
    var children = vnode.children || []
    var newChildren = newVnode.children || []
    // 遍历现有的children
    children.forEach(function (childVnode, index) {
        var newChildVnode = newChildren[index]
        // 两者tag一样
        if (childVnode.tag === newChildVnode.tag) {
            // 深层次对比,递归
            updateChildren(childVnode, newChildVnode)
        } else { 
            // 两者tag不一样
           replaceNode(childVnode, newChildVnode) 
       }
    }
)}