达达快送小程序性能优化实践
-
背景
1.1 面临什么问题
-
问题识别
2.1 “碎片化”优化
2.2 性能指标难保持
2.3 小程序性能分析
-
关键动作
3.1 静态资源优化
3.2 接口聚合&&时序优化
3.3 启动页功能模块梳理&调整
3.4 小程序最佳实践
-
总结&规划
4.1 成果
4.2 规划
1. 背景
达达集团是中国领先的本地即时零售与配送平台,达达快送小程序作为公司重要的 ToC 业务入口,对外提供帮取帮送、跑腿帮买、帮排队等服务,承担了 ToC 业务较高部分的订单量。对于小程序而言,性能至关重要。
-
用户打开率偏低。以微信平台为例,打开率是指在小程序冷启动的情况下,点击小程序到首屏渲染完成的百分比。代码包越大,启动耗时越长,白屏时间越久,用户越可能因为失去耐心而退出小程序,在转化率不变的情况下,订单量越低。
-
性能不达标。如果性能不达标会带来以下两点影响:
-
影响小程序流量获取
-
影响用户体验,进而降低用户留存
不同平台小程序性能指标构成不尽一致,但主要有三个维度考虑:启动性能、网络性能、运行性能,其中网络性能主要包含请求耗时及请求错误率,和接口质量以及用户网络状况强相关,所以此次性能优化的重点放在启动性能以及运行性能。
2. 问题识别
2.1 “碎片化”优化
“碎片化”主要指优化动作过于离散,不连贯无法形成体系。
过去在启动速度不达标时,也会按照小程序平台开发者工具建议优化,额外也有些分包、缩包、图片压缩等动作,但有局限性无法形成合力,依旧存在无法解决的问题:
-
优化效果有限,无法达到「优秀」官方评测结果;
-
启动速度在需求迭代中反弹;
-
过于定制化无法陈定并固化至后续需求研发中;
2.2 性能指标难保持
增加启动阶段的理解、利用好开发者工具、借鉴官方启动优化实践,通过常规动作大概率可以实现一段时间启动性能指标的下降。但要实现指标长期稳定,依旧存在问题:
- 指标监控体系缺失:小程序后台虽然会提供性能数据,但数据散落在各个平台,很难在业务迭代中持续关注并跟进。另外一旦指标波动,用户第一时间就会感知,体验下降,而研发是“后知后觉”的;
- 业务需求迭代包体积增加:过去3个季度,达达快送小程序的发单场景由 2个 -> 8个;
- 碎片化:“碎片化”的优化实践,不成体系无法系统性解决性能问题;
- 团队成员变动:相关文档少,人员变动时无法有效沉淀;
- ......
3. 关键路径
虽然不同平台对小程序启动耗时的各有一套定义,但往往也只是对启动耗时终点定义不一致,小程序的启动流程非常类似,大致会包含以下四步:
-
小程序相关信息准备
-
框架资源准备
-
业务资源准备
-
加载渲染
-
FCP :首次绘制任何文本,图像,非空白canvas或SVG的时间点;
-
TTI :度量从页面开始加载到主要的子资源加载完成并且能够快速可靠地响应用户交互的时间;
- setUp:应用初始化,主要是db查询时间;
- Update:查询更新信息,对比差异;
- offine: 下载离线包;
- waitLoadApp:解压,等待渲染
- 静态资源优化,控制总包、主包、分包大小;
- 接口聚合,合并接口并尽可能串行改并行,缩短最短请求路径;
- 启动页模块调整,分析启动页功能模块,在不影响业务流程前提下,优化交互方式,提升页面渲染性能;
- 沉淀传承,总结归纳最佳实践,持续保持优秀性能指标
3.1 静态资源优化
- 整个小程序所有分包大小不超过 20M;
- 单个分包/主包大小不能超过 2M。
3.2.1 包体积优化
核心思路:从包加载时机和包体积大小两个维度思考。对应动作分别是:预加载、缩包、分包异步化。
-
预加载
:主要指微信小程序平台,通过平台提供的预拉取能力,在不影响主包的情况下将分包加载时机提前,提升分包页面的加载速度。
- 缩包
-
分包:将相对独立的页面和组件拆分到分包,可以解决主包体积受限问题。但随着业务的迭代实际开发中会遇到主包即使页面很少也会过大,经过长期的实践沉淀出一套适合我们的分包策略。
-
无用文件、函数、样式剔除:业务迭代无法避免会产生无用文件,小程序可以入"lazyCodeLoading":"requiredComponents"(在开启「按需注入」特性的前提下,「用时注入」可以指定一部分自定义组件不在小程序启动时注入,而是在真正渲染的时候才进行注入),我们也可以用工具分析没有用到的文件和依赖,进行删除
- 延迟加载 :分包一定程度可以缩减主包体积,但也遇到:
-
插件无法实现分包处理
-
多业务的分包难以划分
所以我们采用了分包异步化(该能力目前只在微信小程序中有)使用componentPlaceholder占位,异步拉取分包加载,完成分包下载和注入后,将占位组件替换成真正的组件。
3.2.2 图片资源处理
-
压缩图片
:上传的图片可以使用二倍图(当设备像素比很大时,图片会被放大,而放大会让图片看起来模糊。为此,我们可以使用二倍图的方式来提高图片的清晰度)并且压缩,大图片不要超过100KB。利用一些压缩技术对图片进行压缩推荐 https://tinypng.com/ ,压缩尺寸可观,但对图片显示质量影响甚微。
-
小图片使用IconFont
:避免小图片作为CDN的情况,因为HTTP一个域名下有请求并发限制,过多图片会造成队头阻塞。可以使用IconFont和base64等方式。
-
大图片统一使用CDN
:在图片处理中,因为我们遇到情况是大部分都是运营配置的图片,可以在配置的后台限制大小,自动转化成webp格式(webp它可让网页图档有效进行压缩,同时又不影响图片格式兼容与实际清晰度,进而让整体网页下载速度加快)。
-
懒加载图片
:屏幕外的图片可以使用懒加载,只有下拉之后才去加载对应的图片。
-
实时性较低的接口缓存结果 :可以采用的策略是,频繁使用的数据缓存到内存,实时性较低的结果缓存到本地,下次启动可以读取到内存中继续使用,也可以给缓存设置一个时效,更新本地缓存
- 减少接口串行:接口没有依赖关系的可以考虑使用Promise.all
- 预拉取首屏资源:预拉取能够在小程序冷启动的时候提前向第三方服务器拉取业务数据,当代码包加载完时可以更快地渲染页面,减少用户等待时间
- 接口合并,优化速度:分析接口询问后端是否可以合并接口,优化稍慢的接口
小结: 在分析接口层面时,我们整理了小程序首页的接口调用的时序图:
- 优化前: 总共用户进入小程序后 21个(埋点接口和个端差异接口未计算在内)
- 优化后: 总共用户进入小程序后 17个(埋点接口和个端差异接口未计算在内)
3.3 启动页模块调整
- 首页
-
首页预先拉取了金刚位,banner,广告位等资源,因为实际调用中获取这些资源链路比较长,我们冷启动中获取资源在小程序启动直接渲染,在真实调用中去比较数据的变化,增量更新,虽然首页是千人千面的页面,但是绝大多数内容是类似的,提前渲染可以提升总体加载时间;
-
对于获取位置信息是比较耗时的,我们在用户授权后缓存了结果到内存,并且存入本地设置了一个时效,在用户下次打开时,未过期的情况下可以直接读取本地I/O。考虑到位置信息需要调用多个接口获取且较慢,所以提升较大;
-
在分析包大小时发现主包存在过大的SDK,但没有办法去除,或者利用plugin方式加载。最后是将涉及插件的SDK拆到分包中利用分包异步化去加载;
- 首页弹窗逻辑之前有5种+,通过让后台承担更多的业务逻辑,可以节省小程序前端代码量,以前的做法是前端从不同接口获取资源,然后通过优先级去展示,前端做很多逻辑处理,现在有后端实现了一套广告位系统,减少了接口调用和逻辑判断。
- 物流详情页
- 针对天降红包,因为他串行3个接口,影响较大。我们执行了发券用户操作之后去加载红包;
- 针对地图的展示,去除了非必要的路径规划,折叠展示了地图
- 本次业务流程的优化,在支付宝小程序总体降低了400ms
3.4 最佳实践
“突击优化”一定程度上能够提升性能指标,但持续保持性能指标的稳定则功在平时。因此除了固化一些专项优化策略,比如:分包策略、图片处理策略等,还需要沉淀更多实践并传承。
- 建立指标监控体系: 主要包含:包大小、图片大小、启动耗时、评测结果。过去研发对于指标的波动是“后知后觉”的,指标监控利于研发尽早发现问题,减少对用户影响。
-
规范发版 : 除了代码包体积代码注入、首屏渲染之外,发版频率等因素也会影响小程序启动耗时,通过与业务方确认在不影响小程序正常功能迭代的前提下,约定每周2次固定发版。
要提升渲染性能就不得不了解小程序的架构:
可以从图中看出,由于渲染层与逻辑层分开,一个小程序有多个界面,所以渲染层对应存在多 webview 。这两个线程之间由 Native 层进行统一处理。无论是线程之间的通讯、数据的传递、网络请求都由Native层做转发。所以如何减少数据交互的 频率 和 大小 是提升渲染性能的关键。
-
合并setData调用
:尽可能合并setData的调用,将多次setData合并为一次,可以模仿vue的nextTick写合并的逻辑(调用 setState 时提供的对象会被加入到一个数组中,当下一次事件循环执行的时候再把这些对象合并一起,通过 setData 传递给原生小程序),支付宝小程序可以调用batchedUpdates来批量更新,减少调用次数;
-
减少setData大小
:传输的数据量越多,线程间通信的耗时越长,渲染速度就越慢,我们的做法是将不设计渲染的数据放入_data中;
-
去掉不必要的属性节点 ,绑定属性数据大小 :在xml中绑定属性data-xxx,尽量减少数据的大小,视图层会把事件 target 和 dataset 数据传输给逻辑层。那么,当自定义数据量越大,事件通信的耗时就会越长;
-
JSAPI调用限制
:一些同步API会阻塞渲染,启动页面减少使用 Sync 结尾的同步 API ,getSystemInfoSync, getStorageSync,setStorageSync。可以缓存结果,或者使用异步获取数据。原则就是:减少使用,缓存结果。
-
依 赖库优化: 删除了埋点框架中一些非必要的同步API的获取操作,或者延后执行,将频繁调用的API结果缓存到内存中,减少调用
-
......
我们来梳理一下整体的优化思路,如下图所示:
- 微信平台
- 启动耗时降低20%;
- 过去两个季度,启动耗时长期维持在优秀;
- 打开率提升0.5%
-
支付宝平台
- C端支付宝小程序 【达达快送】启动耗时降低40%,现在维持在优秀水平。
- 服务竞争力指数 SCI (支付宝平台):提升至满分。
经过本次优化微信和支付宝小程序都达到了官方的优秀指标,优化的经验适用所有小程序。但如何监控启动速度,如何结合自身业务定义小程序性能指标,出现问题及时告警,能够定位具体的原因,需要建立一套自己监控体系,持续保持小程序性能。
参考资料
[1]小程序启动流程介绍:
https://developers.weixin.qq.com/miniprogram/dev/framework/performance/tips/start_process.html[2]支付宝小程序性能优化原理及手段:
https://open.alipay.com/portal/forum/post/103201033
[3] 小程序启动流程介绍:
https://developers.weixin.qq.com/miniprogram/dev/framework/performance/tips/start_process.html