小程序开发完成后,开发者需要将代码包上传到小程序管理后台上线,这时候我们会发现,小程序后台对开发者上传的代码包有严格的大小要求:本地代码超过2M就会限制上传
。
1、单个分包\主包大小不能超过2M
2、 整个小程序所有包大小不能超过20M
这种情况下,我们需要将小程序划分成不同的子包,在构建时打包成不同的分包,用户可以在使用时根据实际需要进行加载,这样可以优化小程序首次启动的下载时间
。
使用分包加载的小程序中包含一个主包
、一个或多个分包
,所谓的主包,放置默认启动页面和TabBar页面
,以及一些所有分包都会用到的公共资源和JS脚本
;分包中的内容根据开发者的配置进行划分。
下面这张图是我创建的一个支持分包操作的小程序项目结构:
如上图所示,我用微信开发工具创建了一个QuickStart项目
,然后在根目录
下增加了packageA
和packageB
两个分包文件夹,这两个文件夹就是要进行分包操作的部分。
我们通过在app.json文件中subpackages字段中配置项目分包结构:
{
"pages":[
"pages/index",
"pages/logs"
],
"subpackages": [
{
"root": "packageA",
"name": "packageA",
"pages": [
"pages/cat" ]
}, {
"root": "packageB",
"name": "packageB",
"pages": [
"pages/apple"
]
}
]
}
root:分包根目录
name:分包别名,在分包预下载会用到
pages:分包页面路径,相对分包根目录
independent:分包是否是独立分包
1、配置subpackages
字段后,开发工具会按照subpackages配置的路径打包
,subpackages字段的每个子元素会根据对应配置打包成一个分包
,subpackages配置之外的目录将被打包到主包中
2、app.json文件中最外层的pages字段配置的是主包的页面路径
3、subpackages的根目录不能是另一个subpackages内的子目录
,这句话的意思是,小程序的子包中不能再嵌套子包(禁止套娃~)
4、TabBar
页面必须在主包内
子包可以引用主包中的公共文件,也可以引用自己的包内文件,但不可以引用另一个子包内的文件。
资源文件、template、JS脚本都需要遵守这一引用原则(分包隔离原则),但是在分包异步化时JS脚本不受限制
独立分包一种特殊类型的分包
,它可以独立于主包和其他分包运行。从独立分包中的页面进入小程序时,不需要下载主包,当用户进入普通分包或者主包所属的页面时,主包才会被下载。
由于不需要首先下载主包,独立分包可以很大程度上提高分包页面的启动速度
通过在app.json文件中的subpackages字段中配置independent字段
来声明对应分包是否为独立分包,在下面的示例代码中,packageB分包就被声明为独立分包:
{
"pages":[
"pages/index",
"pages/logs"
],
"subpackages": [
{
"root": "packageA",
"name": "packageA",
"pages": [
"pages/cat" ]
}, {
"root": "packageB",
"name": "packageB",
"pages": [
"pages/apple"
],
"independent": true
}
]
}
1、独立分包可以独立于主包和普通分包运行,所以独立分包中不能有依赖主包和其他分包的内容
(JS文件、template、WXSS等),也就是说,公共样式对独立分包无效
2、App只能在主包中定义
,不能再独立分包中定义
3、独立分包运行时,App可能没有注册 (用户从独立分包进入小程序时,主包不存在,所以也就没有App对象),独立分包中的getApp()可能无法获得App对象
。所以开发者无法通过App对象实现独立分包和其他包之间的全局变量共享,为了解决这个问题,getApp支持allowDefault参数
,在App未定义时返回一个默认对象,当主包加载时,默认对象中定义的属性会被覆盖合并到真正的App
中。
// 独立分包中有以下代码:
const app = getApp({allowDefault: true}) // {}
app.data = 456
app.global = {}
// app.js
App({
data: 123,
other: 'hello'
})
console.log(getApp()) // {global: {}, data: 456, other: 'hello'}
如同上面两段代码所示,小程序由独立分包中的页面进入,这时程序没有加载主包,所以没有App对象,在代码中使用getApp方法可能会出现问题,这时候我们在getApp方法时使用allowDefault
参数,之后就可以正常使用app
对象了;
当主包加载后,App对象被注册,这时候我们在独立分包中定义的属性会被覆盖合并
到主包的App中。
1、进入小程序某一个页面时,自动预下载可能需要的分包,这样可以提高分包页面加载速度
2、由独立分包页面进入的小程序,也可以从独立分包中预加载主包
3、分包预下载目前只支持通过配置方式使用
4、同一个分包的页面合计只有2M的预下载限额
如下面代码所示,在app.json中设置分包预下载时,需要配置两个字段:
subpackages字段
,在这个字段中进行分包设置;
preloadRule字段
,在这个字段中进行预下载设置,preloadRule字段的值是一组key-value
,其中key的值是可以触发预下载的页面路径,value的值是在此页面下的预下载配置,配置的字段包括连个:
packages
:字符串数组,是需要预下载分包的root
或者name
,如果值为_APP_表示预下载主包
network
:字符串,指定可以预下载的网络条件(all:不限网络,只要有网就可以触发预下载;wifi:WiFi下会触发预下载){
"pages": ["pages/index"],
"subpackages": [
{
"root": "important",
"pages": ["index"],
},
{
"root": "sub1",
"pages": ["index"],
},
{
"name": "hello",
"root": "path/to",
"pages": ["index"]
},
{
"root": "sub3",
"pages": ["index"]
},
{
"root": "indep",
"pages": ["index"],
"independent": true
}
],
"preloadRule": {
"pages/index": {
"network": "all",
"packages": ["important"]
},
"sub1/index": {
"packages": ["hello", "sub3"]
},
"sub3/index": {
"packages": ["path/to"]
},
"indep/index": {
"packages": ["__APP__"]
}
}
}
分包异步化是一个比较新的特性,是为了解决分包加载中出现的一系列问题而出现的。
原有的分包隔离机制(指分包之间无法相互引用)导致各分包之间无法引用自定义组件或JS代码,导致分包出现一系列难题。
分包异步化能力打通了不同分吧之间的引用关系,支持跨分包组件、跨分包方法,我们可以通过分包异步化
这一特性,通过一些配置和接口使部分内容可以跨分包使用(等待下载,异步使用)
自定义组件的跨分包使用是借助占位组件
来实现的,在其他分包没有加载时,通过渲染占位组件替代未下载分包的自定义组件,等分包下载完成后进行替换。
在下面这个配置中,button 和 list 两个自定义组件是跨分包引用组件,其中 button 在渲染时会使用内置组件 view 作为替代,list 会使用当前分包内的自定义组件 simple-list 作为替代进行渲染;在这两个分包下载完成后,占位组件就会被替换为对应的跨分包组件
// 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"
}
}
JS代码跨分包使用时,为了避免出现阻塞,需要使用异步获取引用。
// 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'
})