收藏
回答

【卡顿闪退】嵌套使用过多自定义组件会卡顿闪退?

问题模块 框架类型 问题类型 终端类型 微信版本 基础库版本
框架 小程序 Bug 客户端 7.0.3 1.9.91

- 需求的场景描述(希望解决的问题)


我们的小程序比较复杂,嵌套使用了很多自定义组件,自定义组件间传递的数据有些是较大的对象。现在发现性能很差,用户经常反馈卡顿闪退,做性能分析的时候提示过多地调用了setData,但其实我们的setData并不多,只是自定义组件层级多而已。


我理解是不是因为自定义组件的数据传递机制有点问题?


举个例子:页面A中使用了自定义组件C1,自定义组件C1中又使用了自定义组件C2 ......


自定义组件的数据传递机制是不是这样的:


1、页面A通过调用setData,把A的数据stringify后传递给了页面A的wxml(从js线程 -> 渲染线程)


2、页面A的wxml发现使用了组件C1,于是把要传递给C1的数据parse之后传递给C1的js线程(从渲染线程 -> js线程)


3、组件C1的js接收到数据之后,需要把数据传递给组件C1的wxml,于是又stringify一次(从js线程 -> 渲染线程


4、组件C1的wxml发现使用了组件C2,于是把要传递给C2的数据parse之后传递给C2的js线程(从渲染线程 -> js线程


5、组件C2的js接收到数据之后,需要把数据传递给组件C2的wxml,于是又stringify一次(从js线程 -> 渲染线程


如此类推。。。。。。


我的问题是:

1、我理解的数据传递过程对么,是这样实现的吗?(代码片段里做了个简单的验证,同一份数据传递之后确实不一样了)


2、如果1的假设是对的,那可不可以通过用app.globalData.xx之类的方式来做数据传递呢?要setData的数据放到app.globalData.xx里,然后在自定义组件中去监听app.globalData.xx的变化(js线程 -> js线程)是不是可以节省下一趟的stringify、parse的开销?


3、如果2的假设是对的,getApp的开销有多大,是不是可以忽略不计的?


4、如果上面的假设没错,那这是不是做性能优化的一个思路呢?

最后一次编辑于  03-28  (未经腾讯允许,不得转载)
邀请回答
复制链接收藏投诉关注问题回答

2 个回答

  • LastLeaf
    LastLeaf
    03-29

    不,你的理解完全不对。自定义组件的数据传递没有跨线程调用。会有对象深复制,但是除非你传的属性真的非常非常复杂、传递的层数非常多,这里的开销很难达到“卡顿”、“闪退”的程度(所以我不建议先从这个角度开始排查)。如果你要尝试避免深复制来看看优化效果的话,可以考虑将数据传递的方式改为 selectComponent 来直接传递(但我真的不建议先尝试这个)。


    通常卡顿、闪退的问题,通常是因为页面的总节点数量太大,达到数千个,低端机难以处理。


    如果怀疑和 setData 调用有关的话,还有另一种可能的原因:有些时候有过多的 setData 被间接触发,导致计算量很大甚至死循环或者无限递归。比如,一次 setData 触发了子组件的生命周期或者 observer 等,导致子组件触发一个事件返给父组件,父组件处理事件时又再次 setData ,类似这样的过程发生很多很多次,就会出现卡顿之类的情况。

    03-29
    赞同 3
    回复 9
    • 卢丑丑
      卢丑丑
      03-29

      感觉还是有点模糊。再问一下


      举个例子:我的页面有一个对象obj,我们假设它stringify后的大小是1kb。


      页面把obj传给了自定义组件A,自定义组件A把obj(深复制后的)传给了自定义组件B。


      自定义组件A的wxml中绑定的数据是来自于自定义组件A还是来自于页面?


      如果是来自于自定义组件A,那自定义组件A是不是需要把深复制后的obj做stringify然后传给渲染线程?那在这个例子中实际上从逻辑线程往渲染线程传的数据是3kb(1份原始的和两份A、B中深复制的)?


      如果是来自于页面,那自定义组件A中深复制的这份obj还有什么意义?

      03-29
      回复
    • LastLeaf
      LastLeaf
      04-01回复卢丑丑

      会变成 3kb 。换而言之,传输延迟会变大。如果传的数据很大(>50k),在界面上展示出来的时间会变晚。但通常不会导致闪退,除非执行这样的 setData 的次数也很多。

      04-01
      1
      回复
    • 卢丑丑
      卢丑丑
      04-02回复LastLeaf

      嗯的明白了,我试着从setData入手吧,感谢🙏

      04-02
      回复
    • 正一🍚筒
      正一🍚筒
      05-16回复LastLeaf

      这样的话, 是不是说, 不建议组件拆的太细啊

      05-16
      回复
    • LastLeaf
      LastLeaf
      05-16回复正一🍚筒

      拆得太细会有点影响性能,但某些情况下又有一些性能优势。一般影响不大的啦。


      自定义组件应该还是优先用于组织代码,怎么拆取决于怎么样组织代码比较好。当然还需要注意一些限制,比如试图封装 <form> <button> 可能导致拿不到 form-id 。

      05-16
      回复
    查看更多(4)
  • 摇了摇头
    摇了摇头
    05-23

    请问楼主优化是否有成效了?

    05-23
    赞同
    回复 9
    • 卢丑丑
      卢丑丑
      05-24

      优化效果一般,除了自定义组件层级可能引起性能问题,还有不定高图文长列表、众多大图片、页面节点太多、页面结构太复杂等等都会影响性能,我还没想到什么有效的解决方案,只能建议说不要把小程序做的太复杂。

      05-24
      1
      回复
    • LastLeaf
      LastLeaf
      05-24回复卢丑丑

      https://github.com/wechat-miniprogram/recycle-view

      这个可以作为参考。

      05-24
      回复
    • 卢丑丑
      卢丑丑
      05-24回复LastLeaf

      这个我看过了,它要求是定高的

      05-24
      回复
    • 卢丑丑
      卢丑丑
      05-24回复LastLeaf

      高度固定的话,可以方便地判断出当前展示哪几个元素。但是不定高的话,如果要知道每个元素的高度,就只能去拿clientRect,这个过程加上双线程之间通信,本身就很影响性能了

      05-24
      回复
    • LastLeaf
      LastLeaf
      05-24回复卢丑丑

      注意你可以在一个 selectorQuery 的一次 exec 中获取很多节点的不同值。这个过程如果还很慢的话,那确实应该优先考虑减少节点数量了。

      05-24
      回复
    查看更多(4)