likes
comments
collection
share

虚拟dom真的快吗?🚀

作者站长头像
站长
· 阅读数 16

前言

无论是Vue还是React,都推崇虚拟dom,社区也在推崇使用虚拟dom。我们通过操作虚拟dom能够避免开发者直接操作真实dom,在虚拟dom上应用各种更新后,由框架来统一完成对真实dom的操作。

一开始我也是按照这种先入为主的既定思想来做事的,认为操作虚拟dom在一定程度上可以帮助我们减轻操作真实dom带来的性能开销。一直以来都是带着疑惑的去相信,虚拟dom确实能高效的完成dom操作,但是我今天似乎不再这么认为了,以下是我做的几项简单的测试:

(PS:本文仅代表我个人观点,文中若有以偏概全的地方、或者描述得不恰当的地方,希望大家包容并指出)

实验环境

  • Vue: 2.7.14
  • React:17.0.2

  • Chrome: 113.0.5672.64(正式版本) (64 位)
  • Microsoft Edge:113.0.1774.42 (正式版本) (64 位)
  • Firefox:113.0.1 (64 位)

由于是在Windows上实验的,就没有测试Safari

开始实验

测试代码

本文针对 "三种浏览器",采用三种不同方式实现 "对dom节点添加 10000 个span元素" 进行简单测试

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>virtual-dom-test</title>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
  <script crossorigin src="https://cdn.bootcdn.net/ajax/libs/react/17.0.2/umd/react.development.js"></script>
  <script crossorigin src="https://cdn.bootcdn.net/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>
  <script crossorigin src="https://cdn.bootcss.com/babel-standalone/6.26.0/babel.js"></script>
</head>

<body>
  <div id="app"></div>
  <script type="text/babel">
    const app = document.querySelector('#app');

    /* 普通js操作 */
    for (let i = 0; i < 10000; i++) {
        const span = document.createElement('span');
        span.innerText = i;
        app.appendChild(span);
    }

    /* Vue */
    const vue = new Vue({
        el: app,
        template: `<div>
            <span v-for="i in 10000">{{ i }}</span>
        </div>`
    })

    /* React */
    function App() {
      return <div>{Array(10000).fill(0).map((_, i) => <span>{i}</span>)}</div>
    }
    ReactDOM.render(<App />, app);
  </script>
</body>

</html>

Chrome

测试前先注释掉相关CDN以及Vue和React的测试代码,将script的type属性去掉或改为text/javascript

打开chrome的测试面板进行 performance 测试

原生js操作

虚拟dom真的快吗?🚀

耗时:

  • js - 17ms
  • style + layout - 71ms

Vue

虚拟dom真的快吗?🚀

  • js - 53ms
  • style + layout - 86ms

React

虚拟dom真的快吗?🚀

  • js - 167ms
  • style + layout - 83ms

Microsoft Edge

接下来轮到 Microsoft Edge 表演

原生js操作

虚拟dom真的快吗?🚀

耗时:

  • js - 13ms
  • style + layout - 109ms

Vue

虚拟dom真的快吗?🚀

  • js - 53ms
  • style + layout - 106ms

React

虚拟dom真的快吗?🚀

  • js - 212ms
  • style + layout - 102ms

Firefox

接下来轮到 Firefox 表演,这个火狐的分析面板属实有点难以看懂🤣

原生js操作

虚拟dom真的快吗?🚀 耗时:

  • js - 14ms
  • style + layout - 112ms

Vue

虚拟dom真的快吗?🚀

  • js - 48ms
  • style + layout - 8 + 28 + 76 = 112ms

React

虚拟dom真的快吗?🚀

  • js - 120ms
  • style + layout - 8 + 28 + 82 = 118ms

统计

累死我了,可算是测完了,以下为汇总数据

虚拟dom真的快吗?🚀

注意

需要注意的是,大多数浏览器为了减少重排重绘,采用了队列化修改和批量执行来优化整个过程,因此才能让以上数据在使用框架与不使用框架几乎一致

同时,获取布局信息的操作,例如:clientHeight、clientWidth、offsetHeight、offsetWidth、scrollTop等,会使队列刷新,立刻进行样式计算和布局计算,例如:

以下操作会耗费性能:

/* 普通js操作 */
for (let i = 0; i < 10; i++) {
  const span = document.createElement('span');
  span.innerText = i;
  app.appendChild(span);
  console.log(app.clientWidth);
}

虚拟dom真的快吗?🚀

可以见到,每轮循环浏览器都会进行重排,因此获取布局信息的操作要结合当前代码慎用,避免过早的render造成性能问题。

总结

经过以上测试发现,真正的 "样式计算 + 页面布局" 在三种操作中的耗时是几乎一致的

在浏览器队列化修改、批量更新策略下,他会将一帧中的所有dom操作收集起来,在js同步代码执行完成后统一处理。所以,虚拟dom并没有比操作真实dom快,只是把对真实dom的操作变成了对虚拟dom的操作,最后还是需要框架统一的去操作真实dom。反而,在内存里构建两颗虚拟树我觉得更消耗时间。