微信小程序主包上限 2MB、整包上限 30MB(个别类目 20MB),这是所有小程序团队绕不开的天花板。我们交付的校园点餐小程序曾把主包压到 1.98MB 的悬崖边,最终通过分包架构降到 0.9MB,启动时间缩短约 40%。本文给出完整可落地的操作路径。

一、先体检:体积都花在哪里了

在微信开发者工具中点击"代码依赖分析",能看到每个文件占比。常见元凶排序:

  • 图片资源占 50% 以上——最典型的问题。本地打包的 PNG/Banner 图动辄几百 KB 一张
  • 未按需引入的组件库——整包引入 Vant Weapp / TDesign,实际只用了三五个组件
  • 重复的工具函数——多个 utils 文件互相拷贝
  • ES6 转 ES5 后的冗余——关闭"上传代码时样式自动补全"等可减少体积

第一步永远是:把所有静态图片迁移到 CDN(对象存储 + CDN 域名),代码里只留 URL。仅此一步通常减少 40%~70% 体积。

// ❌ 错误:图片放进项目 /images 目录随包上传
<image src="/images/banner-home.png" />

// ✅ 正确:CDN 地址,主包零负担
<image src="https://cdn.example.com/banner-home.webp" />

二、分包配置:主包只留"启动必需"

分包的核心思想:主包只放 tabBar 页面和公共代码,其余业务按模块拆进 subpackage。

{
  "pages": [
    "pages/index/index",
    "pages/menu/menu",
    "pages/mine/mine"
  ],
  "subpackages": [
    {
      "root": "pkg-order",
      "pages": [
        "confirm/confirm",
        "detail/detail",
        "history/history"
      ]
    },
    {
      "root": "pkg-admin",
      "pages": [
        "dashboard/dashboard",
        "stats/stats"
      ]
    }
  ],
  "preloadRule": {
    "pages/menu/menu": {
      "network": "wifi",
      "packages": ["pkg-order"]
    }
  },
  "lazyCodeLoading": "requiredComponents"
}

三个关键点:

  • 公共代码放主包:分包可以引用主包的 js/组件,反过来不行。跨分包共享的工具建议通过主包 app.js 挂载或使用分包异步化
  • preloadRule 预下载:用户浏览菜单(主包)时,WiFi 环境提前拉取点餐分包,点"去结算"零等待
  • lazyCodeLoading 必开:默认注入全部自定义组件代码,开启后只注入当前页用到的组件,对页面多的项目立竿见影

三、独立分包:营销页的启动加速器

独立分包不依赖主包,扫码直达时不需要先下载主包。活动页、推广落地页放进独立分包,冷启动速度可以快一倍以上:

{
  "root": "pkg-activity",
  "independent": true,
  "pages": ["invite/invite", "coupon/coupon"]
}

注意约束:独立分包内不能引用主包的公共代码、不能使用 getApp()(需判空),登录态等逻辑要自带。

四、进阶压缩手段清单

手段效果备注
图片转 WebP体积减 30%~50%iOS/安卓基础库 2.9+ 均支持
关闭 sourcemap 上传减 10%~20%项目设置里关闭"上传时带 sourceMap"
组件库按需引入视用量Vant Weapp 天然按需;整包引 UI 的自查
分包异步化主包再瘦身组件跨分包引用(基础库 2.17.3+)
wxml/wxss 混淆压缩减 5%~15%开发者工具"上传压缩"全开

五、上线前的体积纪律

体积管理是持续过程,建议固化两条规则进 CI:

  1. 上传前跑体积脚本:用 miniprogram-ci 的 preview 接口拿到包体积,超过阈值(如主包 1.6MB)直接告警
  2. 图片一律走 CDN 审计:CI 里扫描 .wxml 与 js 中的本地图片路径,发现即构建失败
// scripts/check-size.js(配合 miniprogram-ci)
const ci = require('miniprogram-ci');
const project = new ci.Project({
  appid: 'wxXXXXXX',
  type: 'miniProgram',
  projectPath: './dist',
  privateKeyPath: './private.key',
});
ci.preview({ project, desc: 'size-check' }).then((r) => {
  const size = r.subPackageInfo ?? [];
  console.log(JSON.stringify(size, null, 2));
  // 解析主包大小,超阈值 process.exit(1)
});

小结

包体积治理的优先级:图片出包 → 分包拆分 → 按需注入 → 预下载策略 → CI 体积门禁。按这个顺序执行,2MB 限制从"随时爆雷的炸弹"变成"再也不会看一眼的数字"。