开发者社区> 问答> 正文

什么是 Virtual DOM?为什么 Virtual DOM 比原生 DOM 快?

什么是 Virtual DOM?为什么 Virtual DOM 比原生 DOM 快?


【精品问答】前端面试手册

【精品问答】前端面试手册之React篇

展开
收起
前端问答 2019-12-24 12:31:49 3143 0
1 条回答
写回答
取消 提交回答
  • 前端问答小助手

    大家都知道操作 DOM 是很慢的,为什么慢的原因已经在「浏览器渲染原理」章节中说过,这里就不再赘述了。

    那么相较于 DOM 来说,操作 JS 对象会快很多,并且我们也可以通过 JS 来模拟 DOM

    const ul = {
      tag: 'ul',
      props: {
        class: 'list'
      },
      children: {
        tag: 'li',
        children: '1'
      }
    }
    
    

    上述代码对应的 DOM 就是

    <ul class='list'>
      <li>1</li>
    </ul>
    
    

    那么既然 DOM 可以通过 JS 对象来模拟,反之也可以通过 JS 对象来渲染出对应的 DOM。当然了,通过 JS 来模拟 DOM 并且渲染对应的 DOM 只是第一步,难点在于如何判断新旧两个 JS 对象的最小差异并且实现局部更新 DOM。

    首先 DOM 是一个多叉树的结构,如果需要完整的对比两颗树的差异,那么需要的时间复杂度会是 O(n ^ 3),这个复杂度肯定是不能接受的。于是 React 团队优化了算法,实现了 O(n) 的复杂度来对比差异。 实现 O(n) 复杂度的关键就是只对比同层的节点,而不是跨层对比,这也是考虑到在实际业务中很少会去跨层的移动 DOM 元素。 所以判断差异的算法就分为了两步

    • 首先从上至下,从左往右遍历对象,也就是树的深度遍历,这一步中会给每个节点添加索引,便于最后渲染差异
    • 一旦节点有子元素,就去判断子元素是否有不同 在第一步算法中我们需要判断新旧节点的 tagName 是否相同,如果不相同的话就代表节点被替换了。如果没有更改 tagName 的话,就需要判断是否有子元素,有的话就进行第二步算法。

    在第二步算法中,我们需要判断原本的列表中是否有节点被移除,在新的列表中需要判断是否有新的节点加入,还需要判断节点是否有移动。

    举个例子来说,假设页面中只有一个列表,我们对列表中的元素进行了变更

    // 假设这里模拟一个 ul,其中包含了 5 个 li
    [1, 2, 3, 4, 5]
    // 这里替换上面的 li
    [1, 2, 5, 4]
    
    

    从上述例子中,我们一眼就可以看出先前的 ul 中的第三个 li 被移除了,四五替换了位置。

    那么在实际的算法中,我们如何去识别改动的是哪个节点呢?这就引入了 key 这个属性,想必大家在 Vue 或者 React 的列表中都用过这个属性。这个属性是用来给每一个节点打标志的,用于判断是否是同一个节点。

    当然在判断以上差异的过程中,我们还需要判断节点的属性是否有变化等等。

    当我们判断出以上的差异后,就可以把这些差异记录下来。当对比完两棵树以后,就可以通过差异去局部更新 DOM,实现性能的最优化。

    另外再来回答「为什么 Virtual DOM 比原生 DOM 快」这个问题。首先这个问题得分场景来说,如果无脑替换所有的 DOM 这种场景来说,Virtual DOM 的局部更新肯定要来的快。但是如果你可以人肉也同样去局部替换 DOM,那么 Virtual DOM 必然没有你直接操作 DOM 来的快,毕竟还有一层 diff 算法的损耗。

    当然了 Virtual DOM 提高性能是其中一个优势,其实最大的优势还是在于:

    • 将 Virtual DOM 作为一个兼容层,让我们还能对接非 Web 端的系统,实现跨端开发。
    • 同样的,通过 Virtual DOM 我们可以渲染到其他的平台,比如实现 SSR、同构渲染等等。
    • 实现组件的高度抽象化
    2019-12-24 12:32:58
    赞同 1 展开评论 打赏
问答排行榜
最热
最新

相关电子书

更多
低代码开发师(初级)实战教程 立即下载
冬季实战营第三期:MySQL数据库进阶实战 立即下载
阿里巴巴DevOps 最佳实践手册 立即下载