背景
- 我们都知道微信小程序有包体积限制,整个小程序所有分包大小不超过 20M,单个分包/主包大小不能超过 2M。然而面对业务的不断更新迭代,代码和资源会越来越多,如果不尽早规划包体积的治理,势必有一天会对业务的发展造成阻碍。所以如何在有效支持业务逻辑的同时,尽量减少资源占用,在小程序开发环境中显得尤为重要。
- 小程序包体积大小也是影响小程序启动速度的重要因素,包体积越小,需要加载的资源越小,启动速度越快。
包体积分析
在遇到主包体积偏大后,我们需要弄明白,主包里有哪些东西?它们为什么这么大?
可以使用原生小程序开发者工具自带的分析工具(或者webpack-bundle-analyzer插件),它可以直观分析打包出的文件包含哪些,大小占比如何,模块包含关系,依赖项,文件是否重复,压缩后大小如何,我们可以做针对性优化。
打开位置如下所示:
利用小程序自带代码质量工具进行检测
利用代码质量检测工具,针对性进行优化
分包优化
如果项目是uni-app构建,则需要开启分包优化选项
在manifest.json开启分包优化选项subPackages: true
...
/* 小程序特有相关 */
"mp-weixin": {
"appid": "wx2f13686a028b0192",
"optimization": {
"subPackages": true
},
}
...
使用分包
普通分包
使用方法:
假设支持分包的 uni-app 目录结构如下:
┌─pages
│ ├─index
│ │ └─index.vue
│ └─login
│ └─login.vue
├─pagesA
│ ├─static
│ └─list
│ └─list.vue
├─pagesB
│ ├─static
│ └─detail
│ └─detail.vue
├─static
├─main.js
├─App.vue
├─manifest.json
└─pages.json
则需要在 pages.json 中填写
独立分包
小程序中的某些场景(如广告页、活动页等),通常功能不是很复杂且相对独立,对启动性能有很高的要求。使用独立分包,可以独立于主包和其他分包运行。从独立分包中页面进入小程序时,不需要下载主包。
分包纬度建议
懒加载,一个模块或一个页面为一个分包,不要把多个模块的页面放在同一个分包中,如潮妆和微商城项目多个营销活动放在了同一个分包中,导致该分包体积越来越大,加载某个营销活动的同时,也会把其他营销活动加载进来。
防止分包资源被打包到主包中
把只被分包使用的资源收集到分包里,比如图片,js模块,组件,npm包 等
例如:src/components目录下的组件都会自动被打包到主包,分包组件应该放在自己分包目录下
资源上传cdn
特别是图片、音频、视频等体积较大的媒体文件,可以移至cdn服务器(阿里云oss文件服务),需要时再下载
页面动态化
把非核心不紧急的页面,转成h5,通过webview来进行显示。一个两个页面看不出什么差别,但是如果有10个8个的就很明显了,起码能节省出几十kb。
数据外置
有时开发中我们会把一些不变的数据放到小程序项目内,比如收获地址模块地址的城市信息,隐私协议等这样的数据尽量能走线上就走线上,当首次加载完后可以缓存到本地。
及时清理废弃资源和代码
已下线或者已弃用的文件资源应及时清理,包括npm包、组件、页面、媒体资源等。若后续需要重新上线/重新使用,可以通过git等版本控制工具找回。这部分资源不需要持续占用代码包空间。
为了免除无法 tree shaking 的烦恼,我们在开发 npm 模块的时候也需要遵循一定的规范,从而减少模块打包后的大小。
去除重复代码
慎用cv大法,抽离封装可复用代码,在已有项目中,发现在业务开发过程中,为了一时之快,相似模块和方法就直接拷贝以前的老代码,导致项目越来越臃肿,难以维护
提取公共模块
- 设计开发层面减少重复,多提取公共模块,减少重复造轮子。
- 业务实现做到通用,提取公共的业务组件,比如不同的活动可以采用统一模板,同一个组件,而不必每次增加新代码。
- 样式层面保持统一,使用统一的基础组件, 比如可以统一弹窗规范,而不引入五花八门的零碎弹窗组件。
慎重用三方插件
- 量少用第三方的插件,比如echart,你可能只要它的1%功能,一个曲线图,却不得不打包它,使整个项目体积骤升。
- 如果确实需要,不要在全局引用注册,在模块页面使用,就在某个页面的分包下注册
不要让你的代码太啰嗦
- 在 JavaScript 代码层面,请审慎的考虑你的代码逻辑。不要把一个很简单的逻辑洋洋洒洒的写了一大堆,请优化和精简你的代码。
- 在视图层面,尽量避免不必要的组件嵌套,能用一个 view 做到的,就不要再多套一层 view。这对减少代码尺寸和代码性能都是有好处的
封面方案(滴滴出行小程序在用方案)
封面方案简单讲,就是做一个带公司Logo的封面作为启动页面,而页面一旦加载,立刻跳转另一个页面,这个页面真正承载业务,且它被放在分包里。
这个操作的意义在于,主包里就只剩下了所有方都要依赖的基础框架/库等,而业务全被抽离到了分包里。
这个方案优秀的地方在于,后续的业务迭代产生的体积增长几乎全是在业务主分包里。而主包的体积在理想条件下是可以长久保持不变的,就不会因为业务需求的不断开发反复导致主包体积临近超标,不再需要为主包体积感到焦虑。
微商城微信小程优化实践
分包优化
分包优化,减少主包体积
条件编译
由于多端采用同一套代码,条件编译显得尤为重要,防止非本端模块和代码打包进来
页面级别条件编译
- 在pages.json进行条件编译
// #ifdef APP-PLUS
{
"root": "pages/setting",
"pages": [
{
"path": "about/detail",
"style": {
"navigationBarTitleText": "详情"
}
},
{
"path": "function-introduced/index",
"style": {
"navigationBarTitleText": "功能介绍"
}
},
{
"path": "function-introduced/detail",
"style": {
"navigationBarTitleText": "功能介绍详情"
}
}
]
},
// #endif
- 其他地方对于在代码中使用条件编译语法
static 目录的条件编译
h5授权文件条件编译
h5授权文件条件只有在h5端才用到,但是项目中被打包到所有端中了
优化后:
static目录条件编译默认会打包在static文件中,而h5授权文件需要放在项目根目录,利用shell.js在打包完成后挪动到根目录
build/h5-build.js
// build/h5-build.js
const path = require('path');
const shell = require('shelljs');
// static 采用目录件编译,h5端才需要校验文件,优化小程序包体积,打包完成后把h5目录校验文件挪到外层static目录下
shell.cp('-R', path.resolve(__dirname, '../dist/build/h5/static/h5/verify/*'), path.resolve(__dirname, '../dist/build/h5'));
shell.rm('-rf', path.resolve(__dirname, '../dist/build/h5/verify'));
package.json
非全局组件从全局组件中挪出到对于页面
强烈建议用WebStorm进行重构,文件位置挪动,代码中的路径自动匹配,删除文件页会做依赖分析,确保被删除的文件没用被依赖
统一函数库、组件
历史遗留问题,同一个工具函数、相似组件造了多个轮子