微信小程序学习日记 L08 分包加载

目前小程序分包大小有以下限制:

  • 整个小程序所有分包大小不超过 20M
  • 单个分包/主包大小不能超过 2M

随着项目的不断迭代,小程序的主包会越来越大, 终究会有一天会超过 2M 的主包限制,首先考虑到的就是图片资源放到 CDN 或者 OSS,起初会很奏效,但是随着一些JS库的加入就没办法了,即使使用了 .min 版本,或者精简了类库代码,主包还会很大,这时候就会用到项目分包了。

使用分包

使用分包很简单,在 app.json 当中使用 subpackages 声明项目分包结构就可以。

{
  "pages":[
    "pages/index",
    "pages/logs"
  ],
  "subpackages": [
    {
      "root": "packageA", // 子包根目录
      "name": "pack1", // 别名,预下载时使用
      "pages": [ // 子包页面(相对于子包根目录)
        "pages/cat", 
        "pages/dog"
      ]
    }, {
      "root": "packageB",
      "name": "pack2",
      "pages": [
        "pages/apple",
        "pages/banana"
      ]
    }
  ]
}

然后只需要注意2点就好了:

  • subpackage 的根目录不能是另外一个 subpackage 内的子目录(可以在主包目录下);
  • tabBar 页面必须在主包内;
  • 不同 subpackage 间的资源无法相互引用,公共的部分可以放在主包内,或者使用 分包异步化 来解决。

有点迷糊的可以直接看官方给到的 分包加载版示例源码

独立分包

独立分包是小程序中一种特殊类型的分包,可以独立于主包和其他分包运行
从独立分包中页面进入小程序时,不需要下载主包。当用户进入普通分包或主包内页面时,主包才会被下载。

在上边的分包基础上,给独立分包的子包增加 independent 属性既可。

{
  "pages":[
    "pages/index",
    "pages/logs"
  ],
  "subpackages": [
    {
      "root": "packageA", // 子包根目录
      "name": "pack1", // 别名,可在预下载时使用
      "pages": [ // 子包页面(相对于子包根目录)
        "pages/cat", 
        "pages/dog"
      ]
    }, {
      "root": "packageB",
      "name": "pack2",
      "pages": [
        "pages/apple",
        "pages/banana"
      ],
      "independent": true
    }
  ]
}

限制和分包一直,并且额外的有3个条件:

  • 独立分包中不能依赖主包中的内容,即不能使用公共的库和公共样式等资源;
  • 在主包加载前无法使用 getApp();
  • 独立分包中暂时不支持使用插件。

这个我暂时还没有用到,具体注意事项可以查看文档 独立分包 | 微信开放文档


分包预下载

开发者可以通过配置,在进入小程序某个页面时,由框架自动预下载可能需要的分包,提升进入后续分包页面时的启动速度。对于独立分包,也可以预下载主包

预下载分包行为在 进入某个页面时触发,通过在 app.json 增加 preloadRule 项来配置。

{
  "pages": [
    "pages/index"
  ],
  "subpackages": [
    {
      "root": "subpackage1",
      "pages": [
        "index"
      ],
    },
    {
      "root": "subpackage2",
      "name": "sub2",
      "pages": [
        "index"
      ],
    },
    {
      "root": "subpackage3",
      "name": "sub3",
      "pages": [
        "index"
      ]
    },
    {
      "root": "indep",
      "pages": [
        "index"
      ],
      "independent": true
    }
  ],
  "preloadRule": {
    "pages/index": {
      "network": "all", // 所有网络环境均进行预加载
      "packages": ["subpackage1"] // 进入 pages/index 时加载 subpackage1 的内容
    },
    "subpackage1/index": {
      "packages": ["sub2", "sub3"] // 进入 subpackage1/index 时加载 subpackage2 和 subpackage3 的内容
    },
    "indep/index": {
      "network": "wifi", // 仅在wifi环境下预加载
      "packages": ["__APP__"] // 进入独立分包 indep/index 时加载 主包内容
    }
  }
}

📍 注意:同一个分包中的页面享有共同的预下载大小限额 2M,限额会在工具中打包时校验。

如,页面 A 和 B 都在同一个分包中,A 中预下载总大小 0.5M 的分包,B中最多只能预下载总大小 1.5M 的分包。


分包异步化

在小程序中,不同的分包对应不同的下载单元;因此,除了非独立分包可以依赖主包外,分包之间不能互相使用自定义组件或进行 require
「分包异步化」特性将允许通过一些配置和新的接口,使部分跨分包的内容可以等待下载后异步使用,从而一定程度上解决这个限制。

✨ 可以先打开官方给的 🔗代码片段 然后对照着看。

1. 跨分包自定义组件的引用

首先是跨分包自定义组件引用,我们看示例代码当中并没有提到 分包下载 的内容,演示的代码片段中也没有,只是使用到了 占位组件 进行替代。

// subPackageA/pages/index.json
{
  "usingComponents": {
    "button": "../../commonPackage/components/button",
    "list": "../../subPackageB/components/full-list",
    "simple-list": "../components/simple-list"
  },
  "componentPlaceholder": {
    "button": "view",
    "list": "simple-list"
  }
}

说明 跨分包引用自定义组件 是不需要单独处理的,小程序会自行下载使用到的跨包组件。我们只需要关注在跨包组件下载完成之前的占位和展示即可了。

2. 跨分包 JS 代码的引用

跨分包 JS 代码引用和上边的跨包组件引用不一样了,提供了专门的 async 方法 或者 使用回调的方式解决。

// subPackageA/index.js
// 使用回调函数风格的调用
require('../subPackageB/utils.js', utils => {
  console.log(utils.whoami) // Wechat MiniProgram
})
// 或者使用 Promise 风格的调用
require.async('../commonPackage/index.js').then(pkg => {
  pkg.getPackageName() // 'common'
})

这个就很容易理解了,需要等待代码加载完成,所以是异步操作,和跨包组件一样,这个也是自动完成的,不需要单独在外部申明。


尾声

其实小程序分包还挺简单的,就是对已经开发完成的项目再进行分包就会比较麻烦,所有的路由和资源加载路径都需要相应调整一遍。

Q1:主包使用到的分包组件是否只能放到主包内?

其实可以从 分包异步化 部分看到,并不需要把跨包调用的内容全部放到公共组件下,对于这些 自定义组件JS 文件 ,我们可以直接放到子包目录下减少主包的体积,然后使用占位组件去预展示就行了,小程序会自动的加载这些内容。

Q2: 主包与分包的体积都在 1M 以内,上传时提示体积过大

不是分包过大,是分包预加载规则设定的有问题,分包设置预加载的分包超过规定大小了,
同一个分包中的页面享有共同的预下载大小限额 2M,这个限制在上边的 分包预下载 中提到了

- 问题来源:主包与分包的体积都在1M以内,上传时为什么会提示体积过大? | 微信开放社区


文档

分包加载 | 微信开放文档
独立分包 | 微信开放文档
分包预下载 | 微信开放文档
占位组件 | 微信开放文档
滴滴出行小程序体积优化实践 - 掘金