最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Virtual DOM 认知误区

    正文概述 掘金(莫得盐)   2020-11-24   468

    在当下最流行的两个前端框架都存在 Virtual DOM 的前提下, 渐渐比较多的听到类似“使用 Virtual DOM 有什么优势?” 的面试题,但一直没有太在意。直到今天在写一个文档时,突让想到要把“为什么需要 Virtual DOM ?”也写进去,待我流畅的写好答案,略一思索——漏洞百出!也不知道是接纳了哪方的知识,让我一直有能轻松回答这个问题的错觉, 其实对于这个问题我是缺乏思考的。

    你或许还不清楚我想说什么,但请耐下心来,先来看看网络上关于此问题的一些见解:

    1. 虚拟DOM同样也是操作DOM,为啥说它快? Segmentfault

    2. Virtual Dom 的优势在哪里? Github

    3. Virtual Dom的优势 掘金

    上面是从 Google 搜索到的三个平台中的分析摘选,总结下来大概四点:

    1. 操作 DOM 太慢,操作 Virtual DOM 对象快
    2. 使用 Virtual DOM 可以避免频繁操作 DOM ,能有效减少回流和重绘次数(如果有的话)
    3. 有 diff 算法,可以减少没必要的 DOM 操作
    4. 跨平台优势,只要有 JS 引擎就能运行在任何地方(Weex/SSR)

    它们的理解正确吗?

    本文测试数据都基于 Chrome 86.0.4240.198

    Virtual DOM 快?

    有人认为操作 Virtual DOM 速度很快?Virtual DOM 是一个用来描述 DOM(注意,并不一定一一对应)的 Javascript 对象,Javascript 操作 Javascript 对象自然是快的。

    但 Virtual DOM 仍然需要调用 DOM API 去生成真实的 DOM ,而你其实是可以直接调用它们的,所有就有一个很有意思结论,正数再小也不可能比零还小——Virtual DOM 很快,但这并不是它的优势,因你本可以选择不使用 Virtual DOM 。除了速度不是优势,Virtual DOM 还有个最大的问题——额外的内存占用,以 Vue 的 Virtual DOM 对象为例,100W 个空的 Virtual DOM(Vue) 会占用 110M 内存。

    内存占用截图:

    Virtual DOM 认知误区

    测试代码:

    let creatVNode = function(type) {
      return {
        __v_isVNode: true,
        SKIP: true,
        type,
        props: null,
        key: null,
        ref: null,
        scopeId: 0,
        children: null,
        component: null,
        suspense: null,
        ssContent: null,
        ssFallback: null,
        dirs: null,
        transition: null,
        el: null,
        anchor: null,
        target: null,
        targetAnchor: null,
        staticCount: 0,
        shapeFlag: 0,
        patchFlag: 0,
        dynamicProps: null,
        dynamicChildren: null,
        appContext: null
      }
    }
    
    let counts = 1000000
    let list = []
    let start = performance.now()
    // 创建 VNode(Vue)
    // 10000: 1120k
    for (let i = 0; i < counts; i++) {
      list.push(creatVNode('div'))
    }
    
    // 创建 DOM
    // 10000: 320k
    // for (let i = 0; i < counts; i++) {
    //   list.push(document.createElement('div'))
    // }
    
    console.log(performance.now() - start)
    

    令人意外的是 100W 个空的 DOM 对象只占用 45M 内存,不清楚在 DOM 属性明显更多的情况下 Chrome 是如何优化的,或则是 Dev Tools 存在问题,希望有人能替我解惑。

    你看 Virtual DOM 不但执行快没有用,还增加了大量的内存消耗,所以我们说它快自然是有问题的,因为没有 Virtual DOM 时更快。

    Virtual DOM 减少回流和重绘?

    也有人认为 Virtual DOM 能减少页面的 relayout 和 repaint ?通常有两个原因来支撑这个观点:

    1. DOM 操作会先改变 Virtual DOM ,所以一些无效该变(比如把文本 A 修改为 B ,然后再修改为 A)就不会调用 DOM API ,也就不会导致浏览做无效的回流和重绘。
    2. DOM 操作会先改变 Virtual DOM ,最终由 Virtual DOM 调用 patch 方法批量操作 DOM ,批量操作就不会导致过程中出现无意义的回流和重绘。

    无效回流与重绘

    第一个观点看着很有道理,但有个问题很难解释:浏览器的 UI 线程在什么时候去执行回流和重绘?要知道现代浏览器在设计上为了避免高复杂度,Javascript 线程和 UI 线程是互斥的,即如果浏览器要在 Javascript 执行期间触发 relayout/repaint 则必须先挂起 Javascript 线程,这是个连我都觉得蠢的设计,显然不会出现在各大浏览器身上。

    事实上也确实如此,无论你在一次事件循环中调用多少次的 DOM API ,浏览器也只会触发一次回流与重绘(如果需要),并且如果多次调用并没有修改 DOM 状态,那么回流与重绘一次都不会发生

    Timeline 截图(没有回流和重绘发生):

    Virtual DOM 认知误区

    测试代码:

    <body>
      <div class="app"></div>
      <script>
        let counts = 1000
        let $app = document.querySelector('.app')
        setTimeout(() => {
          for (let i = 0; i < counts; i++) {
            $app.innerHTML = 'aaaa'
            $app.style = 'margin-top: 100px'
            $app.innerHTML = ''
            $app.style = ''
          }
        }, 1000)
      </script>
    </body>
    

    无意义的回流与重绘

    第二个观点是比较有意思的,虽然看了上面的分析,你应该也知道它是错的,批量操作并不能减少回流与重绘,因为它们本身就只会触发一次。但我还是要列出来证明一下,因为这是我们当下众多前端的一个固有思维,我在准备写这篇文章前问了一下众神交流群的朋友们,他们几乎都掉进了这个认知陷阱中,认为批量操作会减少回流与重绘。

    批量操作并不能减少回流与重绘,原因也和上文一致,Javascript 是单线程且与 UI 线程互斥,所以直接放测试数据:

    Javascript 执行耗时(数据取3次平均值):

    条数/方式1000050000100000
    批量7.0ms28.51ms52.74ms单条9.80ms44.83ms79.46ms

    Virtual DOM 认知误区

    Layout 耗时(数据取3次平均值):

    条数/方式1000050000100000
    批量29.81ms133.01ms271.07ms单条28.08ms133.39ms265.32ms

    Virtual DOM 认知误区

    测试代码:

    <body>
      <div class="app"></div>
      <script>
        let counts = 1000
        let $app = document.querySelector('.app')
        let start = performance.now()
        // 单独操作
        // for (let i = 0; i < counts; i++) {
        //   let node = document.createTextNode(`${i}, `)
        //   $app.append(node)
        // }
    
        // 批量操作
        let $tempContainer = document.createElement('div')
        for (let i = 0; i < counts; i++) {
          let node = document.createTextNode('node,')
          $tempContainer.append(node)
        }
        $app.append($tempContainer)
    
        console.log(performance.now() - start)
      </script>
    </body>
    

    可以看到的是,批量处理和单次处理再 Layout 期间耗时是几乎一致的,虽然在 script 执行阶段还是存在一定的性能优势(大概 30%),但大抵上只要你用好 DOM 操作,批量或不批量带来的性能影响是很小的( 10W 次调用多损耗 27ms )。

    题外话:这里提出一个问题,为什么在 script 执行阶段还是存在一定的性能差距?答案会在晚些时候公布(等我看完这部分逻辑)

    Virtual DOM 有 diff 算法?

    严格来说 diff 算法和 Virtual DOM 是两个独立的东西,二者互相之间也没有充分必要的关联,比如 svelte 没有 Virtual DOM 也有其自己的 diff 算法。

    但由于前端框架存在 Virtual DOM 就总有 diff 算法,并且使用了 Virtual DOM 对 diff 算法也有两个助力:

    1. 得益于 Virtual DOM 的抽象能力,diff 算法更容易被实现和理解
    2. 得益于 Virtual DOM Tree 总是在内存中, diff 算法功能可以更强大(比如组件移动,没有完整的 Tree 结构是不可能实现的)

    diff 算法能减少 DOM API 调用,显然是存在设计和性能优势的,而由于 Virtual DOM 的存在,diff 算法可以更方便且更强大,所以我认同这是 Virtual DOM 的优势,但不能用“Virtual DOM 有 diff 算法”这样的表述。

    Virtual DOM 有跨平台优势?

    跨平台是 Javascript 的优势,比如上文提到的 svelte 一样是存在 SSR 的,就不细说了。

    总结

    本文从互联网上摘选了部分对开发者对 Virtual DOM 优点的认知,也从现实生活中了解到一些误解,总结为 Virtual DOM 的四个“优势”,并分别对这四个“优势”进行了单独分析或举证。

    最终我们识别了几个关于 Virtual DOM 优势误区:

    1. 操作 DOM 太慢,操作 Virtual DOM 对象快 ❌

      Virtual DOM 很快,但这并不是它的优势,因你本可以选择不使用 Virtual DOM 。

    2. 使用 Virtual DOM 可以避免频繁操作 DOM ,能有效减少回流和重绘次数 ❌

      无论你在一次事件循环中调用多少次的 DOM API ,浏览器也只会触发一次回流与重绘(如果需要),并且如果多次调用并没有修改 DOM 状态,那么回流与重绘一次都不会发生。批量操作也不能减少回流与重绘。

    3. Virtual DOM 有跨平台优势 ❌

      跨平台是 Javascript 的优势,与 Virtual DOM 无关。

    我们也提到了 Virtual DOM 真正的优点是其抽象能力和常驻内存的特性,让框架能更容易实现更强大的 diff 算法,缺点是增加了框架复杂度,也占用了更多的内存


    起源地下载网 » Virtual DOM 认知误区

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元