揭秘 Vue 中的 Virtual Dom

前言

Vue.js 2.0引入Virtual DOM,比Vue.js 1.0的初始渲染速度提高了2-4倍,并大大下降了内存消耗。那么,什么是Virtual DOM?为何须要Virtual DOM?它是经过什么方式去提高页面渲染效率的呢?这是本文所要探讨的问题。html

模板转换成视图的过程

在正式介绍 Virtual Dom以前,咱们有必要先了解下模板转换成视图的过程整个过程,以下图(参考《深刻浅出vue.js》):前端

  • Vue.js经过编译将template 模板转换成渲染函数(render ) ,执行渲染函数就能够获得一个虚拟节点树
  • 在对 Model 进行操做的时候,会触发对应 Dep 中的 Watcher 对象。Watcher 对象会调用对应的 update 来修改视图。这个过程主要是将新旧虚拟节点进行差别对比,而后根据对比结果进行DOM操做来更新视图。

简单点讲,在Vue的底层实现上,Vue将模板编译成虚拟DOM渲染函数。结合Vue自带的响应系统,在状态改变时,Vue可以智能地计算出从新渲染组件的最小代价并应到DOM操做上。vue

咱们先对上图几个概念加以解释:

  • 渲染函数:渲染函数是用来生成Virtual DOM的。Vue推荐使用模板来构建咱们的应用界面,在底层实现中Vue会将模板编译成渲染函数,固然咱们也能够不写模板,直接写渲染函数,以得到更好的控制。
  • VNode 虚拟节点:它能够表明一个真实的 dom 节点。经过 createElement 方法能将 VNode 渲染成 dom 节点。简单地说,vnode能够理解成节点描述对象,它描述了应该怎样去建立真实的DOM节点。
  • patch(也叫作patching算法):虚拟DOM最核心的部分,它能够将vnode渲染成真实的DOM,这个过程是对比新旧虚拟节点之间有哪些不一样,而后根据对比结果找出须要更新的的节点进行更新。这点咱们从单词含义就能够看出, patch自己就有补丁、修补的意思,其实际做用是在现有DOM上进行修改来实现更新视图的目的。Vue的Virtual DOM Patching算法是基于Snabbdom的实现,并在些基础上做了不少的调整和改进。

Virtual DOM 是什么?

Virtual DOM 其实就是一棵以 JavaScript 对象( VNode 节点)做为基础的树,用对象属性来描述节点,实际上它只是一层对真实 DOM 的抽象。最终能够经过一系列操做使这棵树映射到真实环境上。node

简单来讲,能够把Virtual DOM 理解为一个简单的JS对象,而且最少包含标签名( tag)、属性(attrs)和子元素对象( children)三个属性。不一样的框架对这三个属性的命名会有点差异。git

对于虚拟DOM,我们来看一个简单的实例,就是下图所示的这个,详细的阐述了模板 → 渲染函数 → 虚拟DOM树 → 真实DOM的一个过程 github

image.png

Virtual DOM 做用是什么?

虚拟DOM的最终目标是将虚拟节点渲染到视图上。可是若是直接使用虚拟节点覆盖旧节点的话,会有不少没必要要的DOM操做。例如,一个ul标签下不少个li标签,其中只有一个li有变化,这种状况下若是使用新的ul去替代旧的ul,由于这些没必要要的DOM操做而形成了性能上的浪费。面试

为了不没必要要的DOM操做,虚拟DOM在虚拟节点映射到视图的过程当中,将虚拟节点与上一次渲染视图所使用的旧虚拟节点(oldVnode)作对比,找出真正须要更新的节点来进行DOM操做,从而避免操做其余无需改动的DOM。算法

其实虚拟DOM在Vue.js主要作了两件事:浏览器

  • 提供与真实DOM节点所对应的虚拟节点vnode
  • 将虚拟节点vnode和旧虚拟节点oldVnode进行对比,而后更新视图

为什么须要Virtual DOM?

  • 具有跨平台的优点

因为 Virtual DOM 是以 JavaScript 对象为基础而不依赖真实平台环境,因此使它具备了跨平台的能力,好比说浏览器平台、Weex、Node 等。缓存

  • 操做 DOM 慢,js运行效率高。咱们能够将DOM对比操做放在JS层,提升效率。

由于DOM操做的执行速度远不如Javascript的运算速度快,所以,把大量的DOM操做搬运到Javascript中,运用patching算法来计算出真正须要更新的节点,最大限度地减小DOM操做,从而显著提升性能。

Virtual DOM 本质上就是在 JS 和 DOM 之间作了一个缓存。能够类比 CPU 和硬盘,既然硬盘这么慢,咱们就在它们之间加个缓存:既然 DOM 这么慢,咱们就在它们 JS 和 DOM 之间加个缓存。CPU(JS)只操做内存(Virtual DOM),最后的时候再把变动写入硬盘(DOM)

  • 提高渲染性能

Virtual DOM的优点不在于单次的操做,而是在大量、频繁的数据更新下,可以对视图进行合理、高效的更新。

为了实现高效的DOM操做,一套高效的虚拟DOM diff算法显得颇有必要。咱们经过patch 的核心----diff 算法,找出本次DOM须要更新的节点来更新,其余的不更新。那diff 算法的实现过程是怎样的?

diff 算法

diff 算法

Vue的diff算法是基于snabbdom改造过来的,仅在同级的vnode间作diff,递归地进行同级vnode的diff,最终实现整个DOM树的更新。由于跨层级的操做是很是少的,忽略不计,这样时间复杂度就从O(n3)变成O(n)。

diff 算法包括几个步骤:

  • 用 JavaScript 对象结构表示 DOM 树的结构;而后用这个树构建一个真正的 DOM 树,插到文档当中
  • 当状态变动的时候,从新构造一棵新的对象树。而后用新的树和旧的树进行比较,记录两棵树差别
  • 把所记录的差别应用到所构建的真正的DOM树上,视图就更新了

image.png

diff 算法的实现过程

diff 算法自己很是复杂,实现难度很大。本文去繁就简,粗略介绍如下两个核心函数实现流程:

  • patch(container,vnode) :初次渲染的时候,将Virtual Dom渲染成真正的DOM而后插入到容器里面。
  • patch(vnode,newVnode):再次渲染的时候,将新的vnode和旧的vnode相对比,而后之间差别应用到所构建的真正的DOM树上。

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
}
复制代码

2.patch(vnode,newVnode)

这里咱们只考虑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) 
       }
    }
)}
复制代码

给你们推荐一个好用的BUG监控工具Fundebug,欢迎免费试用!

欢迎关注公众号:前端工匠,你的成长咱们一块儿见证!

image

参考文章和书籍

相关文章
相关标签/搜索