查看原文
其他

性能翻倍!京东亿级体量小程序优化实践

平台研发 黄宏峰 京东技术
2024-08-24


Tech

导读

本文深入探讨如何通过系统性的优化措施显著提升了京东亿级用户体量小程序的性能。从前端渲染优化等方面,技术团队实施了一系列创新策略,使小程序的性能翻倍。本文将详细介绍这些优化措施的实施过程和取得的成效,为在大规模用户基础上追求极致性能的开发者提供宝贵的实践经验。




01 前言

在今年的敏捷团队建设中,我通过Suite执行器实现了一键自动化单元测试。Juint除了Suite执行器还有哪些执行器呢?由此我的Runner探索之旅开始了!
小程序性能是指小程序在微信APP或者其他宿主APP中加载和呈现的速度,以及小程序对用户交互的响应程度。性能欠缺的小程序渲染和响应速度较慢,甚至会出现无法正常打开小程序的情况,在不同程度上极大地影响了用户体验,从而导致用户流失。

京东购物小程序随着更多业务不断的更新迭代,启动性能逐步下降,微信后台打开率仅86%,每天有几百万的流失。随着互联网人口红利的结束,增量变缓,如何通过技术手段提升启动性能成为重中之重,以便更好地留住来之不易的增量用户,进一步助力业务的成长。



02   性能指标与衡量  

理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将目标页面展示到屏幕。

使用性能指标来评估小程序的加载速度是非常必要的,首先回顾一下京购小程序页面加载的几个关键阶段:

这几个关键阶段的含义如下:

阶段
含义
loadPackage
小程序代码包下载
First Paint(FP)
页面首次绘制
First Contentful Paint(FCP)
首次有内容的绘制
First Meaning Paint(FMP)
首次有意义的绘制
Largest Contentful Paint(LCP)
页面最大内容绘制,达到可交互状态

在以上的几个阶段当中,最注重的是用户从点击小程序开始到页面最大内容绘制 (LCP) 的总耗时。

而烛龙监控平台已经为京购小程序开发者们提供了性能监控指标,可以通过烛龙监控平台看到对应流程的耗时来作为衡量,小程序启动流程如图:


其中关键的几个监控指标字段和对应意义如下:
字段
意义
_perf_downloadPackage代码包下载耗时
_perf_evaluateScript逻辑层JS代码注入耗时
_perf_firstRender页面首次渲染耗时
_perf_route页面切换耗时
_perf_firstContentfulPaint首次内容绘制时间(FCP)
_perf_appLaunch小程序启动耗时
_perf_largestContentfulPaint最大内容绘制时间(LCP)

烛龙监控平台根据以上几个指标权重和每个指标分值加权平均,计算用户体验值评分(UEI),如图:



03   如何进行性能优化

 



理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将目标页面展示到屏幕。    

3.1  代码包体积优化(减少小程序初始化耗时)


    
从上面的京购小程序启动流程图来看,在app.onLauch执行之前,主要耗时阶段为代码包下载代码注入。而代码包下载和代码注入的时长,均和小程序逻辑代码体积有关。一般而言,小程序代码包越大,逻辑层代码体积也越大,因此初始化耗时也更长。这里我们使用到以下技术:
3.1.1 独立分包
「独立分包」可以独立于主包和其他分包运行。从独立分包页面进入小程序时,不需要下载主包。
开发者可以按需将某些具有一定功能独立性的页面配置到独立分包中。当小程序从普通的分包页面启动时,需要首先下载主包;而独立分包不依赖主包即可运行,可以很大程度上提升分包页面的启动速度。
针对于京购小程序首页,利用独立分包开发了京购极速版首页,并逐步投放在发现购物入口。通过和正常包对比,极速版首页的启动耗时总体降低了420ms,优化了14.5%的启动耗时,以上收益来自于Android侧的大盘优化效果。
3.1.2 分包异步化
「分包异步化」将小程序的分包从页面粒度细化到组件甚至文件粒度。这使得本来只能放在主包内页面的部分插件、组件和代码逻辑可以剥离到分包中,并在运行时异步加载,从而进一步降低启动所需的包大小和代码量。
分包异步化能有效解决主包大小过度膨胀的问题。
针对于附近生活圈频道页面,通过使用分包异步化将主包组件移至分包,将附近生活圈所占主包空间从126kb降低至39kb,极大地降低了主包代码体积;公共components,经过首页、购物车、我京等模块的共同努力,通过分包异步化从203kb瘦身到31kb;分类tab,通过分包异步化138kb瘦身到6kb。

3.1.3 分包预下载

在使用「分包加载」后,虽然能够显著提升小程序的启动速度,但是当用户在使用小程序过程中跳转到分包内页面时,需要等待分包下载完成后才能进入页面,造成页面切换的延迟,影响小程序的使用体验。分包预下载便是为了解决首次进入分包页面时的延迟问题而设计的。

值得注意的是,独立分包和分包预下载可以配合使用,对于独立分包,也可以预下载主包。

针对于附近生活圈频道页面将组件进行分包异步化引入之后,降低了附近生活圈频道各个楼层组件渲染的速度,因此通过在首页配置预下载分包组件的方式,通过提前下载附近生活圈分包组件,解决首次进入附近生活圈页面组件渲染延迟的问题。

3.2  代码注入优化


    
3.2.1 按需注入

可以通过小程序配置,有选择性地注入必要的组件代码:

{ "lazyCodeLoading": "requiredComponents"}
「按需注入」启用后,小程序仅注入当前访问页面所需的自定义组件和页面代码。未访问的页面、当前页面未声明的自定义组件不会被加载和初始化,对应代码文件将不被执行,以降低小程序的启动时间和运行时内存。
值得注意的是,插件包和扩展库目前暂不支持按需注入。如果需要实现插件按需加载,可以考虑将插件置于一个分包,并通过「分包异步化」的形式异步引入。
3.2.2 启动过程中减少同步API的调用
在小程序的启动过程中,会进行代码初始化并依次同步执行 App.onLaunch, App.onShow, Page.onLoad, Page.onShow 等生命周期函数,在此期间应尽量减少或不调用同步API。
常见的容易在初始化阶段调用的同步API有:
1、getSystemInfo/getSystemInfoSync,可尝试使用异步版本API getSystemInfoAsync代替;
2、getStorageSync/setStorageSync,应只用来进行数据的持久化存储,不应用于运行时的数据传递或全局状态管理。

3.3  首屏渲染优化


    
页面首屏渲染的优化,旨在让用户更早的看到有内容的页面绘制(FCP)或者有意义的页面绘制(FMP)。
3.3.1 避免引用未使用的自定义组件
在页面渲染阶段是,会初始化读取当前页面配置和全局配置中使用 usingComponents 引用的自定义组件,以及组件所依赖的其他自定义组件,引入未使用的自定义组件会影响页面初始化渲染耗时。
因此,当组件不被使用时,应及时从 usingComponents 中移除。
3.3.2 精简首屏数据
页面首次渲染的耗时与页面的复杂程度呈正相关。在京购小程序很多业务场景当中,小程序渲染的页面高度是超过一屏的,但在用户首次进入页面时,超出屏幕的页面内容并没有实际性的意义,因此只需要优先渲染出可视范围内的内容即可。当页面首屏渲染完毕后,再继续异步渲染剩下的页面内容。
因此可以对页面首次渲染的内容做一定的取舍,即:
1、如果页面从上到下是由多个独立的组件组合而成的,那么可以针对不在首屏范围内的组件进行延迟加载;
2、如果页面是由某个列表项展开构成的,例如首页的feeds流,那么可以通过主动控制列表项的长度进行分页加载,在滑动至接近于底部时再进行更多列表项的加载和渲染。
需要注意的是,与页面渲染无关的数据尽量不放在data当中,避免影响页面渲染耗时。
3.3.3 提前首屏数据请求
由于网络请求都需要一定的时间,但小程序页面渲染的数据却经常需要依赖服务端的接口返回,在服务端接口返回数据之前页面就可能是空白的或者骨架屏。
为了尽可能早发出核心数据请求,可以采用微信小程序提供的能力:数据预拉取。

「数据预拉取」使得可以在小程序启动时,由微信客户端通过微信后台提前向服务器拉取核心业务数据,当代码包加载完成时,在京购首页通过 wx.getBackgroundFetchData 拿到预拉取的数据,便可以更快地渲染出首页,减少用户等待时间,具体核心流程如图所示:

3.3.4 缓存请求数据用于初始渲染
除去上述的数据预拉取能力,微信小程序提供了 wx.setStorage、wx.getStorage 等API来进行本地缓存的读写。
在京购首页中,将上一次读取到的直出接口的数据存储在缓存当中,以便用户在下次初始化首页时,优先从缓存中读取首页直出数据用来快速渲染页面整体视图,待接口真实返回后再进行页面更新。
3.3.5 骨架屏
「骨架屏」用于页面渲染之前,通过一些灰色的区块大致勾勒出页面的轮廓占位,待页面数据加载完成后,再替换成真实的内容。骨架屏能够有效地避免页面在请求过程展示白屏,避免用户误以为加载失败而退出小程序;且能够避免不同楼层异步渲染的过程中上下跳动,影响用户体验。

3.4  发版更新频率等其他优化


    
3.4.1 合理规划版本发布
通过小程序更新机制可以得知,小程序在启动时如果检测到版本更新,会重新获取小程序的基础信息、重新生成初始渲染缓存等操作,从而影响页面启动耗时。过于频繁的发版更新频率可能会导致用户每次重新打开小程序都需要进行小程序的更新,使得平均启动耗时变长。
因此应提前做好版本规划,控制版本发布更新的频率。
3.4.2 优先使用本地版本设置
通过「优先使用本地版本设置」,判断某些较新的小程序版本无需强制用户更新到最新版本。可以在小程序管理后台「设置」-「功能设置」-「优先使用本地版本设置」进行设置,设置后,当用户使用小程序时,客户端会优先打开本地版本代码包。若用户本地版本不低于该版本,则优先使用本地版本,后台异步更新最新版本的代码包。若低于该版本,则优先更新到最新版本。


04   总结和展望  

理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将目

4.1  性能优化总结


    
看到这里,在了解了京购小程序的性能指标和启动流程后,已经能够很容易地从烛龙监控平台中,查看所负责业务对应的页面性能数据,及时发现页面的一些性能问题并及时进行优化。
当然,除去以上阐述的几点前端性能优化策略之外,也可以通过考虑与服务端约定新的数据格式,避免服务端传输无效数据导致响应内容体积过大等思路进行探索。
在经过上述多种优化后,从微信官方后台we分析中的数据可以看出,京东购物小程序的打开率从原先的86%提升到90%以上 ,相比优化之前每天减少近百万用户流失。

下图是内测阶段京东购物小程序优化前后的真机体验对比,从烛龙监控平台上看,首页整体冷启动耗时从4800ms左右降至2500ms以下。目前线上灰度过程中,用户随机50%命中优化后版本,后续会根据业务数据评估,逐步提高灰度比例,全面提高用户体验。

4.2  未来展望


    
基于当前在性能优化路上的探索和实践,结合实际线上的统计数据分析,在后续也仍会针对于「页面首次渲染」等耗时占比较大的流程进行深入的实践,在「渲染性能优化」层面做更多的尝试,从精简业务数据层面、尝试新的渲染引擎Skyline等等,为客户提供更加友好流畅的使用体验。



推荐阅读2023年通天塔搭建页前端性能优化阶段分享
ADR — 轻量级的架构决策记录机制浅入深出的微前端MicroApp

性能加速包: SpringBoot 2.7&JDK 17,你敢尝一尝吗

求分享

求点赞

求在看

打造SAAS化服务的会员徽章体系,可以作为标准的产品化方案统一对外输出。结合现有平台的通用能力,实现会员行为全路径覆盖,并能结合企业自身业务特点,规划相应的会员精准营销活动,提升会员忠诚度和业务的持续增长。底层能力:维护用户基础数据、行为数据建模、用户画像分析、精准营销策略的制定

▪功能支撑:会员成长体系、等级计算策略、权益体系、营销底层能力支持

▪用户活跃:会员关怀、用户触达、活跃活动、业务线交叉获客、拉新促

继续滑动看下一个
京东技术
向上滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存