- 需求的场景描述(希望解决的问题)
我们的小程序比较复杂,嵌套使用了很多自定义组件,自定义组件间传递的数据有些是较大的对象。现在发现性能很差,用户经常反馈卡顿闪退,做性能分析的时候提示过多地调用了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、如果上面的假设没错,那这是不是做性能优化的一个思路呢?
不,你的理解完全不对。自定义组件的数据传递没有跨线程调用。会有对象深复制,但是除非你传的属性真的非常非常复杂、传递的层数非常多,这里的开销很难达到“卡顿”、“闪退”的程度(所以我不建议先从这个角度开始排查)。如果你要尝试避免深复制来看看优化效果的话,可以考虑将数据传递的方式改为 selectComponent 来直接传递(但我真的不建议先尝试这个)。
通常卡顿、闪退的问题,通常是因为页面的总节点数量太大,达到数千个,低端机难以处理。
如果怀疑和 setData 调用有关的话,还有另一种可能的原因:有些时候有过多的 setData 被间接触发,导致计算量很大甚至死循环或者无限递归。比如,一次 setData 触发了子组件的生命周期或者 observer 等,导致子组件触发一个事件返给父组件,父组件处理事件时又再次 setData ,类似这样的过程发生很多很多次,就会出现卡顿之类的情况。
感觉还是有点模糊。再问一下
举个例子:我的页面有一个对象obj,我们假设它stringify后的大小是1kb。
页面把obj传给了自定义组件A,自定义组件A把obj(深复制后的)传给了自定义组件B。
自定义组件A的wxml中绑定的数据是来自于自定义组件A还是来自于页面?
如果是来自于自定义组件A,那自定义组件A是不是需要把深复制后的obj做stringify然后传给渲染线程?那在这个例子中实际上从逻辑线程往渲染线程传的数据是3kb(1份原始的和两份A、B中深复制的)?
如果是来自于页面,那自定义组件A中深复制的这份obj还有什么意义?
会变成 3kb 。换而言之,传输延迟会变大。如果传的数据很大(>50k),在界面上展示出来的时间会变晚。但通常不会导致闪退,除非执行这样的 setData 的次数也很多。
嗯的明白了,我试着从setData入手吧,感谢🙏
这样的话, 是不是说, 不建议组件拆的太细啊
拆得太细会有点影响性能,但某些情况下又有一些性能优势。一般影响不大的啦。
自定义组件应该还是优先用于组织代码,怎么拆取决于怎么样组织代码比较好。当然还需要注意一些限制,比如试图封装 <form> <button> 可能导致拿不到 form-id 。
请问楼主优化是否有成效了?
优化效果一般,除了自定义组件层级可能引起性能问题,还有不定高图文长列表、众多大图片、页面节点太多、页面结构太复杂等等都会影响性能,我还没想到什么有效的解决方案,只能建议说不要把小程序做的太复杂。
https://github.com/wechat-miniprogram/recycle-view
这个可以作为参考。
这个我看过了,它要求是定高的
高度固定的话,可以方便地判断出当前展示哪几个元素。但是不定高的话,如果要知道每个元素的高度,就只能去拿clientRect,这个过程加上双线程之间通信,本身就很影响性能了
注意你可以在一个 selectorQuery 的一次 exec 中获取很多节点的不同值。这个过程如果还很慢的话,那确实应该优先考虑减少节点数量了。