V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Steel
V2EX  ›  React

React、Vue 关于虚拟 DOM 的文章经常有这样的表述:因为直接操作真实 DOM 耗性能。妥否?

  •  
  •   Steel · 2022-04-20 11:32:40 +08:00 · 4021 次点击
    这是一个创建于 980 天前的主题,其中的信息可能已经有所发展或是发生改变。

    学习 React 过程中,发现很多文章说到: 为什么要用 VDOM ,而不是直接操作真实 DOM ?都有类似这样的表述:直接操作真实 DOM 耗性能,细小的改动都可能导致重绘或重排。

    但是如下这样直接操作 DOM ,不比用 VDOM 然后 Diff 后更新 DOM 性能更好吗?

    document.getElementById("divId").innerHTML = "update text";
    

    使用 VDOM 的目的更多的是便于管理更新,而不是性能上的考虑?

    34 条回复    2022-04-20 18:19:41 +08:00
    s0f
        1
    s0f  
       2022-04-20 11:46:19 +08:00
    存在批量修改的情况下,VDOM 可以批量再内存中完成修改,再同步到 DOM 上;
    ShadowPower
        2
    ShadowPower  
       2022-04-20 11:48:10 +08:00   ❤️ 1
    如果这个操作在短时间内做 1000 次,最后再把它改回原样,用 VDOM 就可以绕过所有重排和绘制工作
    许多页面的生成过程,并不能像这个例子一样一步到位
    makelove
        3
    makelove  
       2022-04-20 11:50:09 +08:00
    就问你怎么根据应用状态更新 DOM 吧,要么精细操作,DOM 操作会到处散落且有大量重复片段,要么直接简单无脑全量刷新。
    即不想要手动精细操作,又不想低效全量更新 DOM,那只有 VDOM 或类 solidjs 这种,很明显性能比原始全量更新性能高。
    gzf6
        4
    gzf6  
       2022-04-20 11:52:27 +08:00 via Android
    不谈量级的话这种说法不妥
    summer1874
        5
    summer1874  
       2022-04-20 11:55:21 +08:00
    其实就是性能与可维护性的均衡。
    声明式的更新性能消耗( vue ,react )= 找出差异的性能消耗 ( diff ,vdom )+ 直接修改的性能消耗( document.createElement 等)。只是目前结合心智负担,可维护性等因素综合考虑,虚拟 dom 还是不错的选择。
    murmur
        6
    murmur  
       2022-04-20 11:58:08 +08:00
    vue react 应该都有 batch 操作,先攒够一堆,然后在更新上去
    toesbieya
        7
    toesbieya  
       2022-04-20 11:59:19 +08:00   ❤️ 2
    仿佛在逗我,虚拟 dom 性能永远超不过真实 DOM
    statumer
        8
    statumer  
       2022-04-20 12:00:07 +08:00 via iPhone
    页面上元素少的时候,直接用 DOM 确实有性能优势。比如说写自己博客,直接用 DOM 没什么问题,可以很细粒度的控制状态转移。
    页面上有成百上千个元素的时候,你手算最优状态转移是不可能的。所以你直接操作 DOM 完成的不是最优状态转移,但是 vDOM 算出来的是最优状态转移,而 js 计算的开销比 DOM 操作小很多,这时候 vDOM 就有性能优势。
    jjwjiang
        9
    jjwjiang  
       2022-04-20 12:02:20 +08:00   ❤️ 1
    如果你能精确的明白你哪些 dom 操作会触发重绘,所以能够合并这些操作,那肯定是有性能优势的

    问题是没人能做得到
    statumer
        10
    statumer  
       2022-04-20 12:07:46 +08:00
    状态数的增长也会导致你手算最优状态转移的难度暴增。三个状态互相转换你只需要写 3!=6 个 DOM 状态转移函数(K3 完全图)。7 个状态互相转换需要写 7!=5040 个 DOM 状态转移函数(K7 完全图)。
    ryncv
        11
    ryncv  
       2022-04-20 12:14:04 +08:00   ❤️ 1
    vdom 并不能保证 100%优于直接操作 dom ,还是需要看 diff 算法开销,只能说是很大一部分场景下会更加高效。
    另外还有就是工程化的因素在里面,更多的是更加便捷的开发效率,不会有人为了追求性能去手动去更新 dom 吧?
    otakustay
        12
    otakustay  
       2022-04-20 12:20:00 +08:00   ❤️ 1
    如果没有 VDOM ,要知道一个状态有没有改变,得去 DOM 上拿对应的属性再和最新的状态比对,但 DOM 的读实在太慢了

    https://imgtu.com/i/LrJBee
    egoyau
        13
    egoyau  
       2022-04-20 12:32:47 +08:00   ❤️ 1
    这个问题需要区分场景。
    1 、dom 更新频率低:比如静态页面、广告页面,没有数据更新,一次性渲染。操作真实 DOM 性能优于 VDOM 。
    2 、dom 更新频率高:有较强的交互性,数据更新频率高。操作真实 DOM 性能劣于 VDOM 。
    cyrbuzz
        14
    cyrbuzz  
       2022-04-20 13:04:51 +08:00
    vdom 应该更多的是提供了跨平台的可编程能力,如果仅仅只有直接去操作 DOM 这一种途径,那在非浏览器场景下就不可用,比如服务器端渲染,有 vdom 在其他平台上框架操作的也是 vdom ,只需要实现如何渲染 vdom 就好了。
    sweetcola
        15
    sweetcola  
       2022-04-20 13:17:10 +08:00
    忘了在哪篇 React 官方的文章看到的了,反正就是说“VDOM 是一个错误的说法,它令别人感觉是 DOM”,不知道有没有记错,这一点也在上面的一些回复也得到体现。

    React 更新 DOM 其实也是你这种方法来更新,不然还能怎样用 JS 去更新 DOM 。

    举个例子,面对 100 层深度的 DOM ,第 50 层和 99 层要同时被更新,你会怎样做。`document.getElementById("root").innerHTML = '<div>.........</div>'`吗? React 的话会直接找到需要更新的节点并进行替换。
    agdhole
        16
    agdhole  
       2022-04-20 13:18:28 +08:00
    keithwhisper
        17
    keithwhisper  
       2022-04-20 13:33:14 +08:00
    使用 vdom 还有一个 [我认为最有潜力的] 因素是建立了抽象, 前端不再局限于浏览器, 可以拓展到其他载体的可视化中工程中.
    Steel
        18
    Steel  
    OP
       2022-04-20 13:45:30 +08:00
    @agdhole 这个可以啊!省去中间商赚差价!解决了楼上各位朋友提到的大规模更新的问题,还直接更新 DOM 。
    zilan
        19
    zilan  
       2022-04-20 13:45:51 +08:00
    楼主应该没做过 jquery 时代(以及之前)的前端开发吧
    Steel
        20
    Steel  
    OP
       2022-04-20 13:47:13 +08:00
    @keithwhisper 这个确实如此,RN 的跨平台不正是因为这个 VDOM 吗
    mxT52CRuqR6o5
        21
    mxT52CRuqR6o5  
       2022-04-20 13:50:43 +08:00 via Android
    这是发展时期某些粉丝发表的谬论,vdom 相比直接操作 dom 多了建立 vdom 与 diff 的过程,实践中评论来说 vdom 性能是比不过直接操作 dom 的
    yaphets666
        22
    yaphets666  
       2022-04-20 14:04:20 +08:00 via iPhone
    一次操作肯定直接操作 dom 快啊。vdom 最后也是操作 dom 啊。但是程序怎么可能只操作一次 dom 呢?是成百上千次操作,经过 vdom 整合,性能优势就出来了。
    lscho
        23
    lscho  
       2022-04-20 14:06:23 +08:00
    你这个举例明显不对啊。。。你的这个例子只有更新 DOM ,没有 DIFF 啊。

    举例对比应该举 DIFF 的过程,而不是最后更新 DOM 的操作吧?就算事 VDOM ,最后也是这样更新 DOM 的。
    dudubaba
        24
    dudubaba  
       2022-04-20 14:16:08 +08:00
    其实直接操作 dom 消耗的性能在绝大部分业务系统中都毫无影响,重要的是 react\vue 这种 mvvm 模式带来了全新的开发模式,什么虚拟 dom 啊这类知识点让前端变的“难”了起来,要知道简单的东西都是让人不屑一顾的,要想测底站住脚必须要“难”
    qzsi001
        25
    qzsi001  
       2022-04-20 14:17:26 +08:00
    其实 dom 修改只是一方面,如果是 jq 时代过来的前端,会明显发现一个问题,dom 和数据是不一致的。
    我记得 react vue 刚出来的时候宣传的点都会说到这个问题。
    其中尤其是 vue 刚出来的时候双向绑定带来的便捷感,把传统的 jq 开发模式碾压的死死的。
    打通 dom 和 数据 的同步之后的前端,才真正的有可能大规模的往工程化方向上去靠。
    至于 dom 修改的性能提升 balabala ,其实私以为只是一个便利顺带的好处罢了
    codehz
        26
    codehz  
       2022-04-20 14:22:38 +08:00
    react 用 vdom 的原因是 render 函数要每次渲染都跑一次,不用 vdom 总不能每次都创造一次 dom 元素吧
    而为啥要跑很多次 render 函数这就纯粹是抽象起来方便的因素了——简单说就是代码容易编写,因为每次运行 render 都是创建新的 vdom 树,类似于即时模式渲染的 gui
    可以做到跟踪 dom 元素的话,那当然是这样快,但是这不方便抽象——Svelte 就需要造编译器以支持抽象
    不方便抽象的后果要么是直接放弃,然后为了精细操作,代码就变得冗长且不易维护
    或者就是强行抽象然后每次都重建整个 dom 树(比如直接重建整个列表的所有元素)——这种情况下,那当然就慢于 react 的 vdom 方案了
    提示:
    改 innerHTML 问题很大(即使只改文本),除非只在创建时用,不然看着 fps 爆炸吧)
    要改就改 nodeValue (注意选择到文本节点而不是元素节点)
    KouShuiYu
        27
    KouShuiYu  
       2022-04-20 16:21:53 +08:00
    实际场景是 document.getElementById("app").innerHTML = "。。。。一万个元素。。。。。"!!
    这时候就体现出虚拟 dom 的好处的,它可以在 js 的内存中直接判断过滤掉不需要更新的,这时候就变成了
    document.getElementById("app").innerHTML = "。。。。几个元素。。。。。"
    document.getElementById("app").innerHTML = "。。。。几个元素。。。。。"
    aguesuka
        28
    aguesuka  
       2022-04-20 16:30:49 +08:00
    自己实现一遍 react 就懂了, 如果没有虚拟 dom, 每次状态修改都要重新加载下面的所有组件, 因为我不知道这个 status 是哪些组件的 porps.

    在你的例子里, 假如我有个表格, 数据在 table 的 status 里, 如果没有 vdom, 那么每次数据变化都要重新渲染表格, 但是 vdom 只需要找到被改过的 cell 就行了
    aguesuka
        29
    aguesuka  
       2022-04-20 16:33:54 +08:00
    而且如果没有 vdom 那么每次 setstatus 都要重新渲染, 而 vdom 可以把多次渲染合并为一次
    retrocode
        30
    retrocode  
       2022-04-20 16:36:00 +08:00
    这个要按复杂情况来, 当页面渲染数据很复杂或多的时候, 古早时期的方法是模板渲染然后直接替换, 整个替换,性能可想而知, 即使不用框架,最后还是会被动查出一个与之功能相似的 diff 算法
    DOLLOR
        31
    DOLLOR  
       2022-04-20 17:01:18 +08:00
    简单粗暴的“innerHTML=”看似便捷,不但有注入安全问题,而且还把里面绑定的事件、状态、属性全干掉了。
    这时候你还得一个一个全部重新绑定、设置状态、设置属性。
    huijiewei
        32
    huijiewei  
       2022-04-20 17:10:42 +08:00
    你都 innerHTML 了。还操作 dom 干嘛。直接每次更改 reload 页面算求了
    fayetitus
        33
    fayetitus  
       2022-04-20 17:57:35 +08:00
    React 提这个是有它自己的语境在的。在官方文档中——它甚至有中文——描述了这一设计的动机:
    https://zh-hans.reactjs.org/docs/faq-internals.html
    https://zh-hans.reactjs.org/docs/reconciliation.html
    ebushicao
        34
    ebushicao  
       2022-04-20 18:19:41 +08:00
    直接操作 DOM 是最快的,可以去看下 SoldJS 这种没用虚拟 DOM 的库和 React 的性能对比。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1512 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 17:07 · PVG 01:07 · LAX 09:07 · JFK 12:07
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.