作者:louiszhai,腾讯增值服务项目管理员工
背景
为了满足日益复杂的小程序活动需求,腾讯增值服务项目组开发了一款Ulink活动小程序,该小程序以游戏社交圈为依托,提供游戏玩家基本的社交功能,如发帖、评论、点赞、分享等。
为了支持这些功能,进行了一系列的性能优化改进。Ulink活动小程序共有5个tab,分别提供关注人的feeds信息、所有用户的精品分享,图文发布入口、消息及个人页,如下所示。
开发过程中折腾了各种各样的挑战和难题。其中以性能问题最为棘手,主要有体现在以下几个方面:
- 小程序首次访问较慢
- 大量UGC图片需要上传,上传慢,体验差
- 页面列表较长时,滚动卡顿,不流畅
- 大量图片和视频的展示,容易导致小程序crash
由于tab页较多,内置了很多的图片素材,请求了较多的后台接口,以及用户发布了大图片帖子等原因,导致小程序首次访问较慢,体验非常糟糕。
为此,我们首先进行了加载优化。
加载优化
加载优化的主要思路如下:
该方案分为4步,分别为资源压缩、请求合并、延迟加载、数据缓存。
第一步,压缩依赖库代码,重复使用的模块代码封装成组件、内置本地的图片压缩大小、以及移除掉其他不需要的页面和组件,避免打入总包。
第二步,小图片base64入包,避免发送网络请求,大图片利用腾讯云压缩后下载,同时在微信小程序支持http2之前,我们合并了接口请求,提升了请求加载性能。
第三步,优先加载首屏,对于一些不重要的资源,或者不会出现在首屏的图片和canvas画布,进行了延迟加载,这样就保证了首屏的速度,对于一些性能不佳的安卓机型,canvas画布的延迟加载,效果比较明显。
第四步,核心就是缓存,减少加载,无论是全局配置、首屏列表信息、还是公共图片等,统统缓存起来,保证首先有内容展示。
优化效果如下:
上传优化
优化完加载后,小程序加载速度明显变快,我们突破了第一个难题。很快,我们就注意到,第3个tab - 发布页成为了瓶颈,因为这个页面可以同时上传 9 张相册照片(可拼成九宫格),而现在手机,无论是 android 还是 ios,随便拍一张,可能就有几M,高清一点的,就有十几二十几M甚至更多。在这个基础上,再乘以9,即使是 wifi 高速上传,图片上传过程也将相当缓慢。为了鼓励用户多发帖,多发图,我们必须要解决这个问题。
因此发图之前,需要压缩图片,压缩再压缩,主要思路如下:
用户原始的相册图片比较大,经过一轮qq和微信压缩客户端压缩后,通常大小在1~2M之间,乘以9后,最坏的情况是,有18M的图片需要上传,18M还是太大了,为此,引入了canvas画布,通过对原图的宽高进行等比缩小,进一步压缩图片大小,保证单张图片的大小不超过600k。压缩后的多张图片,再通过并发的请求进行上传,最终完成发布过程。
当然,实际过程远比这个复杂,部分难点如下所示:
经过兼容性测试,我们发现,安卓部分机型绘制出来的图片背景存在黑屏,为canvas添加一层白色的打底绘制后可以解决。小程序中,特别是安卓下,canvas画布不能太大,数量不能多,为避免小程序crash,我们只保留一个canvas,因此只能一张一张的压缩,这里就需要维持一个压缩队列。qq小程序下,我们还遇到了图片的orientation为 left
或 right
时,图片的宽高信息互换了,导致压缩失败,后来在官方同学的帮助下终于解决了问题。
优化效果如下:
渲染优化
上传优化后,单张图片的上传速度提升71%,图片越大、数量越多,叠加效果越显著,基于此,普通用户终于实现了发图自由。用户发图越积极,不仅意味着小程序越活跃,还意味着第2个tab - 发现页内容越来越多,列表滚动越来越卡。我们不得不重视一个问题,那就是页面渲染存在问题。除了滚动卡顿,页面渲染还存在以下几个问题:
- 页面加载较慢
- 页面刷新时,视图抖动
- 下拉加载时,页面内容更新缓慢
渲染优化的主要思路如下:
小程序页面跳转时,有个动画效果,这个效果完成后,才触发页面的onLoad回调,可以充分利用页面切换时的间隙,提前发送页面的请求,从而达到预加载页面的目的。页面切换时间大致如下:
众所周知,小程序采用双线程模型,如下所示:
这使得 setData 操作涉及到了线程间通信,而频繁的 setData,就像堵车加塞一样,将导致两个后果:
一次 setData,除了要进行数据传输,还要进行一次 evaluateJavascript
脚本过程,大量的数据,会增加 webview js 线程的负担。而我们能够操控的各种点击、滚动事件都将拥堵在 webview js 线程上,得不到响应。我们不妨来看下 setData 数据量与传输时间的关系,如下所示:
可以看到,当数据量小于 4kb 时,数据通信较快,单次耗费小于 15ms。因此不必要传输的数据,要去掉,特别是长列表中,与视觉无关的数据积少成多,愈发影响传输执行效率。因此我们可以有几条最佳实践:
- 避免频繁 setData,根本上减少通信次数
- 去掉UI无关信息,仅更新局部的信息,减少 setData 和 dataset 中的数据量,减少单次通信时间
- 页面切后台时,主动降低渲染优先级,暂停 setData 操作,避免跟前台的页面抢资源
前面我们提到了,长列表数据量、dom数量原本就大,有着天然的渲染痛点,因此滚动事件必须加以节流,尽可能避免频繁查询节点信息,仅更新局部可见区域的数据,延迟更新不可见区域的视图等等。除此之外,页面的 canvas 画布设置为 fixed 布局,在 ios 下,也会导致页面滚动卡顿,需要改为 absolute 布局;由于qq小程序的 video 同层渲染支持较晚,早些版本的qq下,页面滚动可能会导致视频错位,或卡顿。
小程序的本质还是基于H5,因此H5的优化策略依然奏效,比如说,减少 dom 的节点数量,以及层级,需要渲染的dom也就少了;事件绑定多利用委托机制,可以减少事件响应;而自定义的组件,减少了数据往上传递的层级,诸如此类的策略,都可以为小程序渲染加速。
优化效果如下:
页面加载时间降低了29%~41%,性能越差的机型,效果越明显。
优化前,FPS帧率波动大,平均帧率40FPS,最低帧率5FPS,优化后,FPS帧率较稳定,平均帧率达到了60FPS,最低帧率也有50FPS。
内存优化
渲染优化后,Ulink活动小程序整体上快了很多。我们注意到,发现页支持无限下拉加载,列表可能很长,随着用户图片增多,有没有可能导致小程序crash?经过测试,性能较差的机型再一次没有让我们失望,不出意料的crash很多次。因此,内存问题一度成为了瓶颈。
怎么优化内存呢?首先要节约内存,然后还要及时释放内存。
节约内存,图片和canvas懒加载是基本策略,安卓下使用webp图片,也能有效的减少25%左右的内存消耗,发现页的长列表图片,使用腾讯云压缩后,下载到的图片尺寸大大减小,进一步减少了内存消耗。但无论怎么节约内存,只要列表在加载新的图片,内存就会增长。因此我们动态移除了屏幕之外的图片,改用了空白节点占位,这个优化策略,在列表滚动时以节流的方式执行,最终保证了图片内存的及时释放。
优化效果如下:
优化后,初始内存降低了56M,图片无限加载时,内存基本不再变化。不妨拿同类产品做个简单的对比,如下所示:
可以看到,Ulink活动小程序,内存峰值比初始值仅高了68M,内存维持在350M左右,而微博小程序,内存峰值比初始值已经高了180M,内存还在持续增长。
以上,是我们解决Ulink活动小程序性能问题的一些优化实践,欢迎大家下方留言交流。
发现产品机会点?试试用户分层
《动物森友会》如何以奖励设计让人喜喜爱爱?
带你了解腾讯最坚实的支撑事业群