小程序优化之旅(二) -- 分包与启动加载优化

2,576 阅读7分钟

一、前情提要

随着小程序承载的业务越来越重,小程序的代码量也越来越多;但是随之而来就是小程序需要加载的内容越发庞大,启动加载的速度也一慢再慢。但是作为一个对前路有着希望的前端工程师的我必须要做点什么避免它沉沦下去。

查阅腾讯微信小程序的官方文档(上面截图)当中描述到,关于小程序启动的过程当中代码包准备小程序代码注入首页渲染的耗时是三个影响小程序启动性能的主要原因。这里就结合着实际的项目情况主要关注点集中在小程序的代码包体积代码注入这两个方面上进行优化改造,提高小程序的启动性能和耗时。





二、小程序启动的优化方向

2.1 分包加载优化

分包接入

这次是建立于上一期的 juejin.cn/post/713991… 的落地之后,因此这里就不再累赘重复关于分包的一些基础知识了,对分包操作感兴趣的可以点进去看看同系列的以往的文章(求求了)。


分包预下载

在小程序接入代码分包能力后,默认不配置预加载功能的情况下,在进入分包的页面当中是需要先下载对应分包的代码,这时候会有一定程度的加载缓冲时间,在网络质量速度一般或者手机性能一般的情况下就有可能加载缓冲时间较长。因此这里可以使用微信小程序提供的分包预下载能力,在手机处理空闲时候提前利用网络预下载配置的分包代码。在后续操作进入分包页面时候再进行代码注入,这样就能减缓到操作跳转分包页面时候网络请求下载代码的压力,继而能够更快速的进入分包的页面,进而提升用户体验。

分包预加载的启用配置:预下载分包行为在进入某个页面时触发,通过在 app.json 增加 preloadRule 配置项控制

{
  "pages": [
    "pages/index",
    "pages/common",
  ],
  
  "subPackages": [
    {
      "root": "common",
      "pages": ["index"],
    },
    {
      "root": "common1",
      "pages": ["index"],
    },
    {
      "root": "common2",
      "pages": ["index"],
    },
  ],
  
  "preloadRule": {
    "pages/index": { // 代表进入访问的页面路径
      "packages": [
        "common"
      ]
    },
    "pages/common": {
      "network": "all",
      "packages": [
        "common1", "common2",
      ]
    },
  }
}

配置项含义简述:

字段类型必填默认值说明
packagesArray进入页面后预下载分包的 root 或 name。APP 表示主包。
networkStringwifi在指定网络下预下载,可选值为: all: 不限网络 wifi: 仅 wifi 下预下载


分包异步化

正常情况下,分包与分包之间是不能够互相使用自定义组件或进行脚本、资源文件的引入;但是微信小程序给我们提供了「分包异步化」的功能,通过一些配置和新的接口,使部分跨分包的内容可以等待下载后异步使用,从而一定程度上解决这个限制。

跨分包自定义组件

一个分包使用其他分包的自定义组件时,由于其他分包还未下载或注入,其他分包的组件处于不可用的状态。通过为其他分包的自定义组件设置**「占位组件」**,需占位组件作为跨分包组件的替代渲染展示,待分包下载完成后再进行替换。

启用异步占位组件(componentPlaceholder)的配置:

{
  "usingComponents": {
    "comp-a": "/comp/CompA",
    "comp-b": "/comp/CompB",
    "comp-c": "/comp/CompC",
    "comp-d": "/comp/CompD"
  },
  "componentPlaceholder": {
    "comp-a": "view",
    "comp-b": "button",
    "comp-c": "comp-d"
  }
}

配置的注意项: 当一个组件被指定为其他组件的异步占位组件时(如上例中的 comp-d),为其指定占位组件是无效的。即一个组件需要作为其他组件的占位组件,则它必须在一开始就是可用的。

跨分包脚本代码

一个分包中的代码引用其它分包的 js 代码时,为了不让下载阻塞代码运行,可以使用异步的形式进行获取引用的结果。

// SubPackageA/index.js


// 使用回调函数风格的调用
require('../common/utils.js', utils => {
  console.log(utils)
}, ({mod, errMsg}) => {
  console.error(`path: ${mod}, ${errMsg}`)
})

// 使用 Promise 风格的调用
require.async('../common1/utils.js').then(utils => {
  console.log(utils)
}).catch(({mod, errMsg}) => {
  console.error(`path: ${mod}, ${errMsg}`)
})

2.2 代码注入优化

按需注入

通常情况下,在小程序启动时,启动页面依赖的所有代码包(主包、分包、插件包、扩展库等)的所有 JS 代码会全部合并注入,包括其他未访问的页面以及未用到自定义组件,同时所有页面和自定义组件的 JS 代码会被立刻执行。这造成很多没有使用的代码在小程序运行环境中注入执行,影响注入耗时和内存占用。

开启代码的按需注入:

在小程序的配置文件当中添加如下代码配置项

{
  "lazyCodeLoading": "requiredComponents"
}

用时注入(组件懒加载)

在小程序开启了「按需注入」特性的前提下,「用时注入」可以指定一部分自定义组件不在小程序启动时注入,而是在真正渲染的时候才进行注入相关的代码,有点类似于组件懒加载的概念和作用。

在已经指定 lazyCodeLoading 为 requiredComponents 的情况下,为自定义组件配置「占位组件」,该自定义组件就会自动被视为用时注入组件。

异步占位组件

相关的概念和配置项在下文的「 2.1 分包异步化 - 跨分包自定义组件 」当中详细讲述,这里就不在累赘描述了。





三、优化成效对比

这里直接使用的是微信小程序的性能统计的一个工具,能够较直观的看到对应小程序启动的参数的统计情况。

下载耗时:

虽然代码包下载的耗时从整体的变化程度曲线来看不是特别明显(思考原因应该是之前已经接入分包了,能拆分的代码逻辑大部分已经拆分出去了,起点已经是削减原主包代码体积后的结果了),但是也还是可以看到线条的大致方向是有降低的趋势,证明还是有一定程度上的优化。这期分包优化部分主要是对分包的加载进行进一步的优化,因此在小程序的启动阶段的主包下载优化可能并不是特别明显。

js 注入耗时:

js 注入的优化正是这期的优化工作的重点,也可以从图中的线条趋势来看明显能够看到向下降低的趋势(从 330ms 到 290ms,减少了 40ms,为原 js 注入耗时的 12% ),也算是经过这次的优化是有取得一定程度的成效的,虽然还不是特别好的成绩。

总耗时:

从统计图当中能够明显的看出经过这期对小程序启动的优化处理,小程序启动的总耗时有了较为明显的降低(2300ms 到 1800ms,减少了 500ms 左右的耗时,为原总耗时的 20% 左右),反馈在优化后小程序的打开启动有了一定程度上的性能上的提升。打开启动小程序的速度快了,用户体验也随之上来了。





参考资料

同系列文章:

相关知识参考资料: