uniapp 分包体积瘦身实战:主包低于 300KB 的拆包思路

2026-08-14 0 595

说个现实问题,微信小程序对主包体积限制一直是 2MB,后来放宽到 4MB,但主包越小越好,毕竟启动速度摆在那。但 uniapp 项目随便一搞,主包就冲上 1MB,再加上 uni_modules 里的库,一编译就是红色警告。我以前也被逼得实在没办法,花了一个周末把项目拆了个干净,主包从 900KB 压到 200KB 左右。今天把这套思路抖出来。

不是什么高深技术,就是分包、预加载、按需注入那点事,但你得真懂里面的门道,不然拆了照样白搭。

1. 先搞懂 uniapp 分包到底咋回事

uniapp 打包成小程序,pages.json 里的 subPackages 就是分包配置。每个分包可以有自己的根目录,独立打包,只有用到里面页面的时候才去下载。比如你的登录页、个人中心页可以扔到一个 subpackage-pages 里。

这里有四个关键点:

  • 主包大小:包含 app.vue、main.js、公共组件、静态资源等,这部分会随小程序启动时全量下载。
  • 分包大小:每个分包不是单独限制 2MB,而是所有分包加起来不能超过 20MB(微信现在很多类目可以 30MB)。
  • 分包预下载:可以在启动后空闲时提前下载某个分包,让用户点进去时不用加载。
  • 独立分包:这个更狠,可以完全不加载主包就跑起来。适合活动页、游戏页。

但很多人遇到的问题是:我把很多页面放到分包里了,主包体积还是很大。那就得看主包里到底还装了什么。

2. 找出主包里的大头

我用的工具很简单:HBuilderX 编译小程序,然后看微信开发者工具的“代码分析”。里面能清楚看到每个包的体积,还能按文件排序。我当时发现主包里最大的居然是 uni_modules 里的几个组件,还有自己的静态图片。

比如有个 uni_icons 字体文件,动不动就几百KB。很多 uniapp 项目其实只用到了其中几个图标,但全量打包了。解决办法是:把用到的图标单独复制到一个公共目录,或者直接用 iconfont,别引整个 uni_icons。

再比如 store 文件里不小心 import 了某个大模块,也会被打进主包。所以在 pages.json 里配了分包,但代码里你在主包里 import 了分包页面的组件,那也会把组件混进去。这个坑特别隐蔽。

我主要干了几件立竿见影的事:

  • 把自定义组件里用到的 uni_modules 库,全部改成自己写的小组件。比如我只用到一个 uni-list,直接手写一个,少打包一堆。
  • 所有图片都上 CDN,本地只放一个 loading 占位图。
  • 清理 main.js 里没用的插件,比如我没用 vuex,就不用引入。
  • tabBar 图标压缩成 WebP,每个 3KB 左右。

3. 拆包的边界:别什么页面都往分包塞

主包里的页面越少越好,但 tabBar 页面必须在主包中。微信规定 tabBar 页面不能放在分包里。所以你的 tab 页只能留在主包,这没办法。但 tab 页用到的子页面,比如列表详情、选择器,都可以塞进分包。

我当时把项目分成几个分包:

{
  "pages": [
    { "path": "pages/index/index" },
    { "path": "pages/me/me" },
    { "path": "pages/order/order" }
  ],
  "subPackages": [
    {
      "root": "pages-extra",
      "pages": [
        { "path": "detail/detail" },
        { "path": "search/search" },
        { "path": "comment/comment" }
      ]
    },
    {
      "root": "pages-activity",
      "pages": [
        { "path": "coupon/coupon" },
        { "path": "point/point" }
      ]
    }
  ]
}

注意 root 不能和主包里的目录同名。这里我把不常用的页面都扔到了 pages-extra 里。

这样分完以后,主包就是三个 tab 页 + 公共组件 + store,其他全在分包里。启动的时候主包下载量小了很多。

4. 用 preloadRule 做分包预下载

虽然分包是懒加载,但如果用户点击某个 tab 后跳转到详情页,那个详情页所在的分包可能还没下载完,然后就会白屏一下。解决办法就是预下载。

微信小程序提供了 preloadRule,可以配置某个页面加载完以后,预下载指定的分包。我在 uniapp 的 pages.json 里是这样写的:

{
  "pages": [
    { "path": "pages/index/index" }
  ],
  "subPackages": [
    {
      "root": "pages-extra",
      "pages": []
    }
  ],
  "preloadRule": {
    "pages/index/index": {
      "network": "all",
      "packages": ["pages-extra"]
    }
  }
}

这样用户一进入首页,空闲就下载 pages-extra 分包。等他从首页点进详情时,分包已经在本地了,秒开。注意 preloadRule 只能在应用启动后的页面里配置,不能配在分包页面里。

这个规则挺实用,但别滥用。如果每个页面都预下载所有分包,那和没有分包也差不多了。我一般只预下载用户最可能点击的下一个分包。

5. 独立分包:让活动页飞起来

如果你的项目里有那种从分享链接直接进入的活动页,强烈建议改成独立分包。独立分包的代码与主包完全隔离,加载的时候只下载独立分包本身,不用先下主包。这样分享出去的页面打开速度会快很多。

配置方法也很简单,只需要在分包配置里加一个字段:"independent": true

{
  "root": "pages-activity",
  "pages": [
    { "path": "lottery/lottery" }
  ],
  "independent": true
}

但独立分包访问主包的东西会有限制:它不能直接使用主包里的自定义组件,需要把组件也复制到独立分包里。我当时为这个折腾了半天,最后发现只要把活动页里用到的组件都放到独立分包目录下,就不报错了。其实这就是一个独立的迷你小程序,有点麻烦但值得。

6. 编译条件:能砍就砍

uniapp 支持条件编译,可以根据平台选择性打包代码。我项目里有些模块只在 H5 用,有些只在小程序用。以前怕麻烦没弄,后来发现只要加上条件注释,代码直接不进入小程序包,体积又小了一截。

比如我在 main.js 里引入一个统计插件,但只给 H5 用:

// #ifdef H5
import stats from '@/common/stats.js'
stats.init()
// #endif

在小程序编译时,这段代码直接没了。说真的,这个功能是 uniapp 最大的良心,一定要养成习惯。

另外,组件里也可以用 #ifdef MP-WEIXIN 来区分微信小程序和其他平台。这样有些微信专属的登录逻辑不会打进来。

7. 主包里的公共代码也要“瘦身”

去掉第三方库以后,主包里还有一个东西容易忽略:公共组件。很多页面都会引一个 common-card.vue,它在主包里。但这个组件里如果又 import 了别的组件,比如一个日期选择器,那这个日期选择器就算只有详情页用,也被算到主包公共代码里了。因为代码重复引用,webpack 不知道把它放哪,只能放在主包。

解决办法是使用分包优化:在 manifest.json 的 mp-weixin 配置里,有个 分包优化 开关,开启后 uniapp 编译时会把那些只被分包引用的资源尽量分到分包去。我用的 3.x 版本默认关闭,需要手动开启。

{
  "mp-weixin": {
    "optimization": {
      "subPackages": true
    }
  }
}

开启之后,效果很明显。比如有一个图片只在分包页面里用,它就会被打到分包里,不会留在主包。

8. 别在 main.js 里一气乱 import

有个坑我印象深刻。为了省事,我在 main.js 里把所有公共组件都 Vue.component 注册了一下,想着全局随便用。结果每个组件都被打进了主包。后来我改成在需要的页面局部引入,主包体积立马降了不少。

如果你真的有特别高频的组件,可以当成 easycom 组件,uni-app 的 easycom 会自动按需加载。我后来把大部分组件都改成 easycom 了,主包只留几个核心的。

9. 最终数据

我拿我的项目举个例子,不是很夸张但可以参考初始状态:

  • 优化前主包:981KB
  • 优化后主包:212KB
  • 分包代码:1.3MB

主包减少大概 78%。启动加载速度明显变快,之前冷启动偶尔会有几秒白屏,现在基本一点就开。果然“包小腰不疼”。

10. 检查你的静态资源

除了 JS,主包里的图片也是大头。尤其是 tabBar 里的图标,微信要求必须本地文件,没法走 CDN。但你可以用压缩工具压成很小的 WebP,图标基本 3KB 一个。还有那些分包里用到的图片,最好都上传 CDN,然后代码里写网络链接。本地图库一多,主包瞬间爆炸。

我写了个小脚本,检查编译后主包里的图片资源,超过 10KB 的全都标注出来,然后一个个处理。现在主包里基本没有超过 5KB 的本地图片了。

11. 注意分包的依赖传播

有时候你以为分好了包,但实际编译后,主包体积没变。原因是分包里的一些模块被主包引用了,导致 webpack 把这些模块提升到主包。比如我在 app.vue 的 onLaunch 里 import 了一个 utils/request.js,而 utils/request.js 又引用了 store/user.js。如果这个 store 只在分包页面里用,那就白搭了。

我的经验是:主包代码里不能直接引用分包页面里的模块或组件。要是公共模块,就放在主包的 utils 里,被多处使用;要是分包私有,放在分包自己的目录下,然后通过相对路径引用。

另外,我还在主包里用了 require.context 去看所有路由,结果它把分包页面也引用了,导致分包页面模块被拉进主包。这个比较坑,但解法很简单:别用 require.context 处理页面路由,老老实实写在 pages.json 里。

12. 在真机上测

开发工具里看体积是一个样,真机上跑又不一样。微信开发者工具可以看到“代码包大小”以及每个分包的大小。测试的时候最好关掉缓存,冷启动一次,看看主包加载时间和分包加载是否正常。

还有一个点:页面跳转时如果分包没下载完,会看到切换动画卡一下。通过 preloadRule 基本能解决,但也要在弱网环境下测一测,毕竟预下载有时候会被用户断网。

写在最后

这些招都是我在实际项目里踩坑踩出来的,不一定适用所有项目,但核心思路是相通的:把主包留给最核心的页面和依赖,其余的全部拆出去。uniapp 的好处是写一套代码,分包配置也是在 pages.json 里搞定,比起纯原生小程序,工作量还是少一些的。

以后如果遇到主包体积超标,别急着删代码,先看看是不是公共依赖被强行拉进了主包,再考虑把页面拆成独立分包。反正我现在每做一个新功能,第一反应就是这东西能扔分包就扔分包。

行了,这篇就到这,希望你们也能把主包控制得死死的。

uniapp 分包体积瘦身实战:主包低于 300KB 的拆包思路
收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

版权声明:
本站资源有的来自互联网收集整理,本站纯免费分享提供学习使用,如果侵犯了您的合法权益,请联系本站我们会及时删除。
本站资源仅供研究、学习交流之用,免费开源项目不代表完全可商用,若商业用途请先咨询开发企业能否商用,否则产生的一切后果将由下载用户自行承担。
原创板块未经允许不得转载,否则将追究法律责任。

淘吗网 uniapp uniapp 分包体积瘦身实战:主包低于 300KB 的拆包思路 https://www.taomawang.com/web/uniapp/2540.html

常见问题

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务