详解Vue中的虚拟DOM

摘要: 什么是虚拟DOM?

Fundebug经授权转载,版权归原作者所有。

前言

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

模板转换成视图的过程

在正式介绍 Virtual Dom之前,我们有必要先了解下模板转换成视图的过程整个过程(如下图):

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

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

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

  • 渲染函数:渲染函数是用来生成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 的抽象。最终可以通过一系列操作使这棵树映射到真实环境上。

简单来说,可以把Virtual DOM 理解为一个简单的JS对象,并且最少包含标签名( tag)、属性(attrs)和子元素对象( children)三个属性。不同的框架对这三个属性的命名会有点差别。

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

Virtual DOM 作用是什么?

虚拟DOM的最终目标是将虚拟节点渲染到视图上。但是如果直接使用虚拟节点覆盖旧节点的话,会有很多不必要的DOM操作。例如,一个ul标签下很多个li标签,其中只有一个li有变化,这种情况下如果使用新的ul去替代旧的ul,因为这些不必要的DOM操作而造成了性能上的浪费。

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

其实虚拟DOM在Vue.js主要做了两件事:

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

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

为何需要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需要更新的节点来更新,其他的不更新。比如修改某个model 100次,从1加到100,那么有了Virtual DOM的缓存之后,只会把最后一次修改patch到view上。那diff 算法的实现过程是怎样的?

diff 算法

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

diff 算法包括几个步骤:

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

diff 算法的实现过程

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

  • patch(container,vnode) :初次渲染的时候,将VDOM渲染成真正的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,欢迎免费试用!

参考

关于Fundebug

Fundebug专注于JavaScript、微信小程序、微信小游戏、支付宝小程序、React Native、Node.js和Java线上应用实时BUG监控。 自从2016年双十一正式上线,Fundebug累计处理了10亿+错误事件,付费客户有阳光保险、核桃编程、荔枝FM、掌门1对1、微脉、青团社等众多品牌企业。欢迎大家免费试用

(0)

相关推荐

  • 干货|图解 Vue 响应式原理

    Vue 初始化 模板渲染 组件渲染 本文 Vue 源码版本:2.6.11,为了便于理解,均有所删减. 本文将从以下两个方面进行探索: 从 Vue 初始化,到首次渲染生成 DOM 的流程. 从 Vue ...

  • 前端进阶-手写Vue2.0源码(三)|技术点评

    前言 今天是个特别的日子 祝各位女神女神节快乐哈 封面我就放一张杀殿的帅照表达我的祝福 哈哈 此篇主要手写 Vue2.0 源码-初始渲染原理 上一篇咱们主要介绍了 Vue 模板编译原理 它是 Vue ...

  • 新兴前端框架 Svelte 从入门到原理

    前端迷 前天以下文章来源于字节前端 ByteFE ,作者雾豹 字节前端 ByteFE字节前端的技术实践分享在这篇文章中,我们将会介绍 Svelte 框架的特性.优缺点和底层原理.本文尽量不会涉及 Sv ...

  • Vue最新技术内容

    Vue.js是一套构建用户界面的渐进式框架. Vue 只关注视图层, 采用自底向上增量开发的设计. Vue 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件.学习Vue.js前,尽 ...

  • 揭秘 Vue.js 九个性能优化技巧

    这篇文章主要参考了 Vue.js 核心成员 Guillaume Chau 在 19 年美国的 Vue conf 分享的主题:9 Performance secrets revealed,分享中提到了九 ...

  • Vue SSR 即时编译技术

    当我们在服务端渲染 Vue 应用时,无论服务器执行多少次渲染,大部分 VNode 渲染出的字符串是不变的,它们有一些来自于模板的静态 html,另一些则来自模板动态渲染的节点(虽然在客户端动态节点有可 ...

  • Vue源码解析,keep-alive是如何实现缓存的?

    前言 在性能优化上,最常见的手段就是缓存.对需要经常访问的资源进行缓存,减少请求或者是初始化的过程,从而降低时间或内存的消耗.Vue 为我们提供了缓存组件 keep-alive,它可用于路由级别或组件 ...

  • Vue的diff算法是如何操作运用的?本文教你

    前言 本文旨在理一下vue中diff算法的主要逻辑和关键细节. 从一个简单的demo切入: p标签渲染一个items数组 <div id="demo"> <p v ...

  • Vue 3 Deep Dive with Evan You

    什么是 DOM? 如果我们把这个 HTML 加载到浏览器中,浏览器创建这些节点,用来显示网页.所以这个HTML映射到一系列DOM节点,然后我们可以使用JavaScript进行操作.例如: let it ...

  • 详解打板中的分歧转一致

    一致性与分歧性的问题是市场交易中需要深度思考的重要问题,理解不好不仅是陷阱,而且会阻碍你对板学的认识! 一致性问题在很多散户的思维中一直是一个没能正确理解的大误区. 什么是打板一致与分歧 什么是&qu ...

  • 罗盘实用详解22 中针消砂

    罗盘实用详解22 中针消砂

  • 罗盘实用详解27 中针与星宿的五行区别

    罗盘实用详解27 中针与星宿的五行区别

  • 详解摄影中的快门速度,帮你轻松拍大片

    打开凤凰新闻,查看更多高清图片 使用快速快门速度捕获的鸟的图像 一个对初学者来说很重要的曝光变量 摄影中三个最重要的设置之一是快门速度,另外两个是光圈和ISO.快门速度决定了两件事:一是改变照片的亮度 ...

  • 八字命理揭秘—八字术语详解(中)

    [从象] 此乃神趣八法之一.算命术中,神趣八法是算命相当重要的方面.其具体内容有:返象.照象.鬼象.伏象.属象.类象.从象.化象.此八法为看命定格之大关键.从象者,如壬水无根,地支全木,谓从木.地支全 ...

  • 详解Qt中的状态机机制(一)

    状态机,简写为FSM(Finite State Machine),状态机由状态寄存器和组合逻辑电路构成,能够根据控制信号按照预先设定的状态进行状态转移,是协调相关信号动作.完成特定操作的控制中心. 在 ...

  • 中考英语核心词汇详解,中考生再次复习,认真看!(I、J、K开头)

    今天老师整理了中考英语复习的重点核心词汇,总结中考核心词汇考点,甄选高频.难记.易混词汇,并辅以最新中考真题,希望帮助考生们抓住要重点.提升效率.前面的发过的部分小编都已经放在了菜单栏哦,不要忘记打卡 ...

  • 齐炳权:详解奇门遁甲中的值符与值使

    文/齐炳权 学习奇门遁甲要理清主次.脉络.<奇门大全>曰:"凡奇门占法,静则只查值符值使,动则专看方向.盖动者机之先见者也." 奇门遁甲局中先看值符,值使,这是纲领性的 ...

  • 多少人不会养鱼死在这张图上,详解鱼缸中的硝化过程!

    金鱼的王者 383篇原创内容 公众号 本文主要将与主题相关的一些专业理论简述一下,鱼缸的硝化(过滤)系统几个关键方面做一下阐述.养鱼先养水,养水就是养硝化系统,而硝化系统下面一张图全部表示. 鱼类杀手 ...