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

一个组件比较多的页面,即使更新一个小组件,也必须走一遍整个页面的 render ,怎么处理呢?

  •  
  •   sillydaddy · 2021-08-05 10:23:38 +08:00 · 3047 次点击
    这是一个创建于 1204 天前的主题,其中的信息可能已经有所发展或是发生改变。
    现在有一个 react 构造的页面,页面上有很多元素。整个页面是根据一个数据文档(data_document),自顶向下构造的构造的。
    自顶向下就是说,使用 data_document.dataA 构造组件 A,data_document.dataB 构造组件 B 。。然后使用 dataA 里面的子 dataAA,构造组件 A 里的子组件 AA 。

    这里有个问题是,当我操作了页面里面很小的一个组件,从而修改了这个组件对应的 data 时,我需要触发页面的根组件 render(),将整个页面构造一遍。我的思考是,这些页面里的所有组件,都是由 data_document 里的某些 data 完全决定的,它们显示状态变了,就意味着 data_document 变了,反过来也是。所有的组件都是没有额外的状态 state 的。所以想要操作某个组件改变它的状态,就要改变 data_document 的某些 data,将新 data 作为 props 再传递给它。由于整个页面的构建是自顶向下构造的,想要改变传递给某个子组件的 props,就需要跑页面根组件的 render()。

    虽然 render 只是重新构造整个虚拟 dom 树而不是真实 dom 树,但如果组件多的话,跑一遍也很费时间。对于这种情况,有什么好的思路吗?
    ( 这里要注意的是,由于多个组件可能会用同一个数据,所以每次的修改都要即时反馈到 data_document 中,这样的话让被修改的组件只更新自己的 state 就行不通了。)
    第 1 条附言  ·  2021-08-05 16:12:52 +08:00
    了解了一遍大家提到的
    Context/memo/PureComponent/ShouldComponentUpdate/immer/Redux/mobx/...
    概念好多哎。。

    综合了解之后,发现大家的回复,是在解决 2 个问题,
    1 是 props drilling——某个子组件的属性,需要由祖宗组件一直传递下去
    2 是没有数据更新时,也要无效地 re-render

    第 1 个问题,对于主题里提到的例子,不是问题。因为数据是分层的,父组件传给子组件的是子级数据,没有重复传递同一数据的嫌疑;假如确实有 props drilling 这个问题,用 Context 和 Redux 解决挺合适的。

    主题里存在的主要是第 2 个问题。我看了一下,发现 memo 挺适合解决的——只要 props 没改变,就可以用之前缓存的渲染结果。这个跟 PureComponent 是类似的(PureComponent 好像 props 和 state 都要判断)。当然,判断 props 是否发生了改变,需要配合 immutable(不可变数据)使用,immutable 让判断数据内容是否发生了改变只需要作 shallow compare(引用比较)即可,哪怕是数据对象里嵌套的深层属性发生了变化。如果用 immutable(不可变数据)的话,自己手写 ShouldComponentUpdate 也不麻烦,PureComponent 连这点麻烦也省去了。

    谢谢大家的回复。现在对 React 这块的性能优化有点底了。
    24 条回复    2021-08-05 20:47:47 +08:00
    shakukansp
        1
    shakukansp  
       2021-08-05 10:32:46 +08:00
    办法就是不用 react……
    kop1989
        2
    kop1989  
       2021-08-05 10:33:08 +08:00
    所以你要去学习 react Context 的使用。
    binaryify
        3
    binaryify  
       2021-08-05 10:36:15 +08:00   ❤️ 1
    人生苦短,我用 Vue
    kop1989
        4
    kop1989  
       2021-08-05 10:38:09 +08:00
    换而言之,如果一个 dom 的数据与逻辑必须强依赖其“父亲”,那其实他就不是一个独立的“组件”,其渲染与数据处理的动作必然和其他兄弟组件有耦合关系(前提是你的组件规划合理)。所以你也应该接受带者父亲整体重绘。
    kop1989
        5
    kop1989  
       2021-08-05 10:46:29 +08:00
    而传统的针对性操作子 dom 组件的方式,其实是你通过人脑处理了兄弟组件的耦合关系。
    而 react 等工程化库的目的,就是将组件间充分解耦。

    解耦的结果必然会出现冗余与浪费。而当前的互联网带宽与客户端性能也禁得起这样的浪费。
    sillydaddy
        6
    sillydaddy  
    OP
       2021-08-05 10:48:21 +08:00
    @kop1989
    不太明白。组件的一个作用是可以复用。这里我看不出父组件与子组件有什么“强依赖”的关系。父组件只是把数据传递给子组件啊。比如父组件 A 下面有子组件 A1 、A2 、A3,子组件 A1 变了,修改 A1 对应的数据 data1,这时 A2,A3 数据根本没有变化,也没有依赖 A1,但是也要重绘。
    kop1989
        7
    kop1989  
       2021-08-05 10:51:44 +08:00
    @sillydaddy #6 所以你的解决方式有两种。

    1 、将 A1 剥离 A 。
    2 、通过全局变量(比如 context )来控制 A1 所需要的数据。

    如果这两者都不能,那就其实说明 A1 与 A,以及 A2A3 其实有耦合关系。只不过未见得是数据上的。
    huai
        8
    huai  
       2021-08-05 10:53:30 +08:00   ❤️ 1
    @sillydaddy 大多时候不会存在啥性能问题,需要的时候 可以看看

    React.memo

    PureComponent

    shouldcomponentupdate
    binaryify
        9
    binaryify  
       2021-08-05 11:01:09 +08:00   ❤️ 1
    用 shouldcomponentupdate 比较数据或者 mobx 自动判断
    XTTX
        10
    XTTX  
       2021-08-05 11:03:08 +08:00   ❤️ 1
    现在都在用 useEffect 替代 cmd,purecomponet 。useCallback 对应 function useMemo 对应运算结果。 建议 op 去学 react hooks, 因为它已经是主流了
    dashixiong1990
        11
    dashixiong1990  
       2021-08-05 11:03:42 +08:00   ❤️ 1
    组件 React.memo
    数据 immer
    XTTX
        12
    XTTX  
       2021-08-05 11:07:19 +08:00
    react hooks 里 包括 useContext, 可以带入 reducer. 你描述的问题就是 react 以前的传统问题,hooks 可以解决。useContext 大部分情况下替代 redux 。你设计好的话,prop drilling 不会是问题。
    DICK23
        13
    DICK23  
       2021-08-05 11:09:59 +08:00
    render 消耗其实不太大,大部分组件哪怕重新走了一遍 render 流程,实际性能影响微乎其微。
    实在不行就手写 memo 的比较函数,手动指定依赖值
    sherryqueen
        14
    sherryqueen  
       2021-08-05 11:13:21 +08:00
    memo 比较函数.
    sweetcola
        15
    sweetcola  
       2021-08-05 11:23:01 +08:00
    可以把数据拆分成几个 reducer(slice) 放进 Redux 里,把 state 下沉到子组件。
    ykrank
        16
    ykrank  
       2021-08-05 11:23:10 +08:00
    你需要的是 mobx 之类吧
    chenpingan
        17
    chenpingan  
       2021-08-05 11:39:03 +08:00
    react.memo 或者 useMemo 都可以解决这个问题
    KouShuiYu
        18
    KouShuiYu  
       2021-08-05 11:50:51 +08:00
    改用 vue 就不会有这个问题,
    charlie21
        19
    charlie21  
       2021-08-05 14:26:33 +08:00
    很典型的问题
    meteor957
        21
    meteor957  
       2021-08-05 15:35:38 +08:00
    引用类型用 useMemo 包裹,配合 React.memo

    用 react 肯定避免不了 re-render,但是渲染耗时大的组件,应该要做性能优化去减少重复渲染。

    普通的组件像 button 那种,不需太关心。
    66beta
        22
    66beta  
       2021-08-05 15:37:02 +08:00
    所以为什么小尤同志敢说 vue 性能略高于 react~
    xutao881
        23
    xutao881  
       2021-08-05 16:14:19 +08:00
    在你的业务场景还没有触及到 react 性能瓶颈的时候,没必要去担心 render 的性能问题,写 class 组件确实有这种层层传递 prop 的问题,更多的时候你应该考虑怎么去提取复杂 prop 到全局管理,如果还是觉得不爽,用 hooks 吧,监听父级变动可以很优雅。
    aaronlam
        24
    aaronlam  
       2021-08-05 20:47:47 +08:00 via iPhone
    楼主自己根据大伙的答案总结还是很到位,很赞!
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2989 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 14:05 · PVG 22:05 · LAX 06:05 · JFK 09:05
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.