小程序性能优化 - 独立分包的应用

随着业务的增长,小程序的代码量逐渐增加,程序也更加的复杂,急需一场手术来让其轻量化。一个是让程序更加轻量,降低开发难度,另外一个是让其打开速度更快,让用户体验得到提高。同时随着更多的业务加入小程序阵营,小程序的管理方面也变成了一个难题。

小程序执行流程

废话不多说先上图

首先说下整个流程,小程序从用户点击到最后呈现大致可以分为几个步骤

  1. 环境初始化与代码下载(并行)
    • 检测程序更新,如果程序更新了则异步静默下载更新小程序(此时打开的还是旧版本,新版本在下次才会生效)
    • 小程序环境初始化(包含WebView渲染引擎初始化和逻辑层的JS引擎初始化),之后是两边分别的基础库的注入
    • 代码包开始下载,如果本地有缓存的代码则直接运行,如果有代码更新则增量更新(这里需要注意把一些库文件,公共方法等单独存放,不要打包到一起)。根据访问的页面路径路由代码包,如果访问的是主包页面则下载主包代码,如果是访问的独立分包页面,则主包不会下载
  2. 首屏渲染
    在代码和环境准备好了以后,小程序开始渲染首屏内容。这里渲染线程和JS代码执行的线程会分别执行不同的任务。
    • 程序先扫一遍页面的JS代码,将包的页面全部注入(可以在vConsole看到日志)。特别注意的是独立分包代码不执行App.onLaunchApp.onShow
    • 依次执行页面的onLoad和onShow事件,并在onShow事件前上报页面UVPV,这个就是MP后台看到的数据
    • 将页面需要渲染的数据准备好之后,渲染线程开始已经渲染好了页面基本的架构,结合JS线程传输过来的数据,进行页面首次渲染
    • 首屏渲染结束后,调起页面的Page.onReady事件钩子
  3. 异步数据返回更新页面状态
    在首屏渲染完成后,页面可能还处于不可用的默认状态,如只是展示了骨架屏,没具体的内容,页面不可操作
    • 此时异步接口返回,更新页面内容,页面真正的可用
    • 各个组件的接口依次返回,页面内容依次呈现

小程序性能指标

微信小程序助手里面有一个性能分析的指标可以给到开发者一些数据的参考,具体到上图的内容的几个部分。

  • 启动总耗时:小程序进入到首屏渲染完成,大致对应Page.onReady事件钩子调起
  • 下载耗时:小程序代码包的下载耗时,这里有全量代码下载和增量代码下载
  • js注入:js注入这里非独立分包代码会有App.onLaunch/App.onShow钩子的调用,独立分包这里没有App所以不调用,直接页面注入然后Page.onLoadPage.onShow调起。间杂的还有页面pv/uv的上报,上报的过程在Page.onShow
  • 初次渲染:与js注入不一样,初次渲染是渲染引擎的一个耗时指标,与其他不对应

误解:启动总耗时(ms)=小程序环境初始化+下载代码包+js注入(加载代码)+初次渲染,这个公式我在一开始也是信奉的,后面发现其实指标没有单纯的加减关系,每个指标都是独立的。公式from京喜小程序open in new window

性能指标的一些难以监控的地方:小程序发布后不会马上影响所有用户,而是在24小时内,慢慢覆盖到所有的用户,而且在一些业务场景下,如我们的业务场景用户一个月可能才来一次,性能变化会更加难以检测到。比如用户打开的还是旧版的小程序,在他下一次打开的时候新版小程序才会运行。

小程序测速系统

小程序测速API与小程序助手数据不一致的问题:小程序测速这里如果有人上报了应该会发现这里的初次渲染耗时比小程序助手的数据大。经证实这里API获取的会比框架底层获取的耗时多一些。所以这个指标可以参考但是不做准。

我们依次上报了一些性能指标,确保整个过程的耗时可控。

  • 页面总耗时
  • 初次渲染耗时
  • js注入耗时
  • 页面代码注入到onReady的时间
  • 各个接口的耗时分布

下载耗时只能在小程序助手里看到,当然可以选择主包/分包的耗时。如果没有独立分包看到的应该是整包的耗时。

优化手段

从执行流程来说,小程序环境下环境启动,代码包下载、CDN网络、运行机制都是微信统一控制的,开发者能插手的环节不多。开发者能影响的只有自己的业务代码部分,入手的优化手段大致可以这么来分

  • 缩减代码体积
    • 去除无效的页面、工具代码、npm包等,精简代码体积
    • 图片CDN存储
  • 分包/预加载分包
    • 非首页代码分包
    • 预加载分包减少跳转时候的等待耗时
  • 独立分包
    • 将独立的页面分割开来,与主包代码互不影响
    • 受限于TCP请求耗时,暂时独立分包代码的降低对下载耗时的影响可能不会那么大。以后的独立分包优势会越来越明显

缩减代码体积

几年开发迭代下来,程序耦合了几个活动的页面,字体包、大量的图片运用。这里梳理了小程序现有的所有页面,与产品沟通进行删减。

  • 页面数量43个缩减到26个,降幅40%
  • 主包大小1796k下降到1025k,降幅42.9%

代码逻辑优化

  • 发现公共组件组件因为支持的特性越来越多且没有开关,所以耦合了很多的请求在里面,其他页面用到了公共组件,对应的公共组件的请求也被重复调用。此处增加开关控制
  • 小程序对异步的请求接口有限制,页面请求超过10个导致主要接口被阻塞。我们这里是因为有很多上报的请求阻塞,将上报请求后移就可以解决,且上报请求可以在minxin里面集中处理
  • 小程序用到了很多配置,各个配置关系独立,在各个组件或者逻辑依次调起。合并配置请求可以减少请求数量同时加快请求获得(配置内容很少,却占用了请求数量,同时http耗时也消耗了)

独立分包

我们对一些页面进行了独立分包的应用,这里可能一开始大家听到很多框架不支持独立分包。这是因为框架编译代码的时候,会依据入口代码去找寻依赖,而独立分包相当于是两个入口去编译,单纯的文件存放会出现引用路径异常的问题。

如把pages单独存放到一个文件夹act,最后打包出来的act/pages会引用到dist/npm的东西,而独立分包是不允许引用主包代码的。所以这种机制不成立。

- dist
    - pages
    - act
        - pages
    - npm
- src
    - pages
    - act
        - pages
- node-modules

基于独立分包的理论,其实可以实现工程的混合

我们把页面拆分为多个工程,在构建阶段使其合并,同时将代码注入形成最后的提审版本的代码包。各个工程独立开发,在最后发布阶段组装到一个小程序,这样就实现了多个小程序的管理。
同时代码包互相独立,访问A页面的用户不会下载B页面的代码。在一个大型的小程序下,这种方式可以更大程度的利用好独立分包的特性。单独开发了一个合包工具open in new window,可以支持本地或者CI流程的运用,欢迎大家拍砖。

Last Updated:
<manfred>峯</hu>
欢迎关注微信公众号 【Big前端】无广告,无软文,就是这么傲娇。直推一线大厂高质量内容,不局限于前端·后台·运维相关,还包括房价🏠、信用卡💳等内容也可内推一线大厂腾讯阿里字节,对腾讯字节比较熟悉,简历可以发给我,我会给你介绍一线大厂的情况,让你更加了解一线大厂