虚拟Dom真的能够提高性能么

什么是虚拟Dom?

当说起vue和react时候,大家都不免会提到一个概念,就是Virtual DOM(虚拟Dom)。那么,这个虚拟Dom到底是个什么东西,为什么这两个伟大的框架都要使用呢。

首先Virtual DOM是一个映射真实DOM的JavaScript对象,如果需要改变任何元素的状态,那么是先在Virtual DOM上进行改变,而不是直接改变真实的DOM。当有变化产生时,一个新的Virtual DOM对象会被创建并计算新旧Virtual DOM之间的差别。之后这些差别会应用在真实的DOM上。

首先我们举个例子:

<ul class="list">
    <li>item 1</li>
    <li>item 2</li>
</ul>

对于上面的这个Dom树,我们可以简单的用javascript进行描述。

{
    type: 'ul',
    props: {
        'class': 'list'
    },
    children: [
        {
            type: 'li',
            props: {},
            children: [
                'item 1'
            ]
        },
        {
            type: 'li',
            props: {},
            children: [
                'item 2'
            ]
        }
    ]
}

当然,上面的这个Dom树是比较简单的Dom,真实的Dom树远比这个要复杂的多。但是其本质上都是一样的,就是一个嵌套着数组的原生对象。

当新一项被加进去这个JavaScript对象时,一个函数会计算新旧Virtual DOM之间的差异并反应在真实的DOM上。计算差异的算法是高性能框架的秘密所在,React和Vue在实现上有点不同。

Vue宣称可以更快地计算出Virtual DOM的差异,这是由于它在渲染过程中,会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树。

而对于React而言,每当应用的状态被改变时,全部子组件都会重新渲染。当然,这可以通过shouldComponentUpdate这个生命周期方法来进行控制,但Vue将此视为默认的优化。

为什么要用虚拟Dom?

这个问题,其实就是为了对比虚拟Dom和真实Dom的区别,以及使用虚拟Dom有什么优势。很多人都觉得虚拟Dom比真实Dom快。但是真是如此么?

首先我们举个例子:向页面中插入一个元素,不管是按钮还是别的,我们会怎么去做:

document.getElementById('box').innerHTML = '<button>按钮</button>'

但是如果我们向一个盒子中添加10000个button呢,这个时候我们会怎么去做;

var _d1 = new Date();
for (var index = 0; index < 10000; index++) {
    var button = document.createElement('button');
    button.innerText = '按钮'
    document.getElementById('box').append(button);
}
var _d2 = new Date();
console.log(_d2 - _d1)

我们可以一个一个去网页里添加,但是当我们执行这段代码的时候,我们会发现,添加的耗时已经,打印出来了,但是浏览器,还要一会儿才会输出内容。其实这个过程是在进行重排重绘,对于有些浏览器来说,会在我们添加完每一个具有占位的dom的时候,都会进行重排和重绘,我么知道,重排和重绘其实代价是非常的昂贵的。因此我们需要对上述的代码进行优化,优化的结果如下:

var _d1 = new Date();
var _dom = ''
for (var index = 0; index < 10000; index++) {
    _dom += '<button>按钮</button>'
}
document.getElementById('box').innerHTML = _dom;
var _d2 = new Date();
console.log(_d2 - _d1)

我么对比一下上面的两组结果第一个是40ms,第二个是16ms,经过这么对比,我们明显能够发现,其实后者的效率要高于前者。

如果我们创建了这10000个按钮,我们希望对其中的某些按钮进行修改,一种方案就是我们将所需要进行修改的按钮,进行重新生成,组织成dom,然后通过innerHTML插入到页面中,进行重排和重绘;另外一种方案就是我们可以通过找到对应的按钮进行修改,然后在将修改结果逐个设置上去,然后发生重排和重绘,这样的性能的消耗其实也是很严重的。

var _d1 = new Date();
var buttons = document.getElementById('box').childNodesbuttons[100].style.backgroundColor = 'red'
buttons[100].style.width = '500px'
var _d2 = new Date();
console.log(_d2 - _d1)

针对上面的种写法其实对于浏览器来说是很快的。但是既然用实体Dom很快,为什么还要用虚拟Dom呢,首先我们还是从重排和重绘的角度说起,针对于上面的这个操作,我们更新视图,其实是发生了一次重排,和两次重绘。一次重排,就是我们设置了一个按钮的宽度buttons[100].style.width = '500px',两次重绘第一次,是我们设置按钮的背景色,第二次其实是我们设置了按钮的宽度引起了重排,重排引起了重绘造成的。这只是一个简单的视图更新。但是如果对于一个复杂的视图进行更新的话,可能发生的重排和重绘的次数更多。但是我们并不关心它到底发生了多少次重排和重绘,我们只关心最后的结果。但是中间的这些重排重绘的结果我们是不是能避免就避免了,毕竟重排和重绘的代价是很大的。

如何减少重排和重绘呢有以下几点方法:

  1. 隐藏元素,应用修改,重新显示
  2. 使用文档片段,在当前Dom之外构建一个子树,再把拷贝回文档
  3. 将原始元素拷贝到一个脱离文档的节点中,修改副本,完成后再替换原始元素
  4. 使用虚拟Dom

其虚拟Dom的本质就是前面2,3的一个方案,简单的说就是虚拟Dom主要对处理重排和重绘操作做了一个统一(由于经验和经历等各方面的原因,并不是所有的前端同学,都知道怎么去优化重排和重绘的功能,也不是所有的前端优化重排和重绘的的方法是一致的,因此,虚拟Dom的出现,是对这些工作做了一个统一)。

虚拟Dom真的能够提高性能么?

其实大家都知道JS的执行效率是不如C++的,所以说同样的算法的,js的查找Dom的速度其实是不如C++的,也就是说不如实体Dom的;另外一方面,在使用的过程中,我们需要先将实体Dom转换成虚拟Dom,在处理完成之后,再将处理完成的虚拟Dom转化为实体Dom,这个过程中所消耗的性能也同样很多。但是为什么大家都说虚拟Dom比实体Dom快呢(或者说是虚拟Dom能够提高性能)?其实根本原因是就是使用了虚拟Dom能够减少实际Dom的操作次数,减少回流和重绘的次数。

换句话说就是虚拟 dom 相当于在 js 和真实 dom 中间加了一个缓存,利用 dom diff 算法避免了没有必要的 dom 操作,从而提高性能

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

使用diff算法比较新旧虚拟DOM—-即比较两个js对象不怎么耗性能,而比较两个真实的DOM比较耗性能,从而虚拟DOM极大的提升了性能。

接下来我们继续从操作的角度来聊虚拟Dom和实体Dom

  • nnerHTML: render html string O(template size) + 重新创建所有 DOM 元素 O(DOM size)
  • Virtual DOM: render Virtual DOM + diff O(template size) + 必要的 DOM 更新 O(DOM change)

Virtual DOM render + diff 显然比渲染 html 字符串要慢(渲染Html字符串不会进行对比两个真实的Dom,因此少了Diff的这一步。)。但是!它依然是纯 js 层面的计算,比起后面频繁的通过js去操作 DOM来说,依然便宜了太多(js操作Dom,首先要解析js,然后调用浏览的api,但后获取结果,返回给js这个过程其实也是有代价的)。可以看到,innerHTML 的总计算量不管是 js 计算还是 DOM 操作都是和整个界面的大小相关,但 Virtual DOM 的计算量里面,只有 js 计算和界面大小相关,DOM 操作是和数据的变动量相关的。前面说了,和 DOM 操作比起来,js 计算是极其便宜的。这才是为什么要有 Virtual DOM:它保证了

  1. 不管你的数据变化多少,每次重绘的性能都可以接受
  2. 你依然可以用类似 innerHTML 的思路去写你的应用。

但是针对一个特别大的项目,而且交互复杂的项目来说,我们面临着两个问题,一个是可维护性,一个是代码的执行性能。针对于代码执行的性能来说,我们可以通过纯手工的优化 DOM 操作,针对于每一个组件我们都可以进行优化,并且构建一个应用。但是那样又有什么用,最后搞得整个项目一团糟,最后完全失去了可维护性,针对于维护性我们可以通过使用框架的虚拟Dom进行提高项目的可维护性,但是因为我们的虚拟的 DOM 操作层需要应对任何上层 API 可能产生的操作,它的实现必须是普适的,所以会丢失部分性能。因此在超高性能和可维护性方面,我们选择了比较折中的办法就是使用虚拟dom,然后生成框架。

所以说为什么会使用虚拟Dom

  1. 减少不必要的重排和重绘(提高性能)
  2. 提高项目的可维护性(会降低部分性能,但是对于可维护性的角度来说,这部分性能的损耗是能够接受的)

小结

如果你的应用中,交互复杂,需要处理大量的UI变化,那么使用Virtual DOM是一个好主意。如果你更新元素并不频繁,那么Virtual DOM并不一定适用,性能很可能还不如直接操控DOM。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
虚拟DOM(Virtual DOM)是一种在内存中用 JavaScript 对象表示网页的 DOM 结构的技术。通过使用虚拟DOM,可以在保持页面功能不变的同时,提高网页性能的主要原因如下: 1. 批量更新:虚拟DOM能够将多个DOM操作批量处理,减少了直接操作实际DOM的次数。通过比较虚拟DOM树和实际DOM树的差异,只对需要更新的部分进行实际DOM操作,从而减少了浏览器的重绘和回流次数。 2. 减少重绘和回流:虚拟DOM可以通过比较更新前后的虚拟DOM树差异,只更新需要变化的部分。这种优化方式可以减少浏览器对页面进行重绘和回流的次数,从而提高网页性能。 3. 提高渲染效率:虚拟DOM可以在内存中进行操作,而不是直接操作实际DOM。在虚拟DOM中进行各种计算和操作要比直接在实际DOM中进行快得多。这样可以提高页面的渲染效率,减少用户在页面上的等待时间。 4. 跨平台支持:虚拟DOM是对实际DOM的抽象表示,在不同平台上运行的应用程序可以共享相同的虚拟DOM操作逻辑。这种跨平台支持使得开发者能够更轻松地开发和维护多平台的应用程序。 需要注意的是,虚拟DOM并不一定总是能够提高网页性能。在某些情况下,虚拟DOM的额外计算和比较操作可能会带来一定的性能开销。因此,在实际应用中,仍然需要根据具体情况综合考虑使用虚拟DOM的优缺点,并进行性能测试和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值