第5章 5.2 小程序分包加载

🎯 上一章我们搞定了微信小程序的原生能力,像是获取用户信息、地理位置这些"手机自带的功能"。但你有没有遇到过这种情况——小程序功能越加越多,安装包越来越大,用户点开转圈转半天,最后直接跑了?

这就是这一章要解决的问题:小程序分包加载。

说白了,就是让小程序"化整为零",该用的功能才加载,不用先不加载。用户点进来秒开,体验直接拉满。


🎯 开场 3 分钟:为什么要学这个?

场景带入

你做了一个"校园二手交易"小程序,功能模块有:
- 首页商品列表
- 发布商品
- 消息聊天
- 个人中心
- 管理员后台

结果安装包 5MB,用户打开要等 8 秒,点击"我的"直接卡住...

痛点来了:
1. 小程序主包超过 2MB 直接审核被拒
2. 功能全塞主包,加载慢到哭
3. 用户只用到 1 个功能,却要等所有功能都加载完

学完本文能解决

  • ✅ 主包超 2MB 被拒的问题(拆成多个分包)
  • ✅ 首屏加载慢的问题(按需加载)
  • ✅ 用户只用到部分功能却\n\nSimple tech illustration expla\n\nAI comic creation scene, creat\n\n要全部下载的问题

🧱 基础 25 分钟:核心概念

5.2.1 什么是分包?

生活类比:

想象你去住酒店。酒店主包(主楼)只提供前台、走廊这些基础服务。你的房间在副楼(分包),健身房在另一栋楼(另一个分包)。你入住的时候不需要等所有设施都准备好,只用拿到你的房间钥匙就行。

小程序的分包就是这样:
- 主包:小程序的"骨架",包含启动画面、首页、所有页面都需要的公共资源
- 分包:各个功能模块,按需加载

5.2.2 为什么要用分包?

3 个必须用分包的场景:

场景 不用分包 用分包
安装包 3MB+ 直接被微信拒绝上架 拆成 3 个 1MB 分包,秒过审
功能模块独立 全量下载,加载 8 秒 按需加载,2 秒进首页
多人协作开发 合并代码冲突到崩溃 每人负责一个分包,互不干扰

5.2.3 怎么用分包?(uniapp 配置)

uniapp 配置分包只需要修改 manifest.json 和页面路径结构。

项目结构:

项目根目录/
├── pages/                    # 主包页面
│   ├── index/
│   │   └── index.vue
│   └── manifest/
│       └── manifest.vue
├── pages-subpack/            # 👈 分包目录(注意这个目录名)
│   ├── goods/                # 👈 商品分包
│   │   └── list.vue
│   └── chat/                 # 👈 聊天分包
│       └── index.vue
└── manifest.json

步骤 1:在 manifest.json 配置分包

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

这行配置告诉 uniapp:"我要用分包功能,给我打开开关。"

步骤 2:在 pages.json 配置分包路径

{
"pages": [
{
  "path": "pages/index/index",
  "style": {
    "navigationBarTitleText": "首页"
  }
}
],
"subPackages": [
{
  "root": "pages-subpack/goods",
  "pages": [
    {
      "path": "list",
      "style": {
        "navigationBarTitleText": "商品列表"
      }
    }
  ]
},
{
  "root": "pages-subpack/chat",
  "pages": [
    {
      "path": "index",
      "style": {
        "navigationBarTitleText": "消息"
      }
    }
  ]
}
]
}

代码解释:
- subPackages:分包的配置数组
- root:分包的根目录路径(相对于项目根目录)
- pages:该分包下的页面列表

5.2.4 分包的加载规则

生活类比:

你去医院看病,主楼(主包)有挂号和分诊台。你跟护士说"我要看眼科",护士指给你"眼科在东院区 3 楼"——这时候才会去眼科(分包)。

小程序的加载规则:
1. 用户打开小程序,先加载主包
2. 主包加载完成后,按需加载用户访问的页面所在分包
3. 已加载的分包会被微信缓存,下次访问直接用

5.2.5 独立分包(高级配置)

什么是独立分包?

普通分包依赖主包,但独立分包可以独立运行!

生活类比:

普通分包像是酒店的房间——你得先经过大堂(主包)才能进房间。

独立分包像是民宿——你有钥匙就能直接进,不用经过酒店前台。

使用场景:
- 广告页、启动页(用户强点广告直接进,不用等主包)
- 分享裂变页(扫码直接进指定页面)

配置独立分包:

{
"subPackages": [
{
  "root": "pages-subpack/ad",
  "pages": [
    {
      "path": "splash",
      "style": {
        "navigationBarTitleText": "广告页"
      }
    }
  ],
  "independent": true  // 👈 加这个配置
}
]
}

关键就在 independent: true 这一行。

5.2.6 分包预下载

什么是分包预下载?

用户在浏览首页时,偷偷提前把可能用到的分包下载好。

生活类比:

你去超市之前,老婆已经在手机上看好要买什么、放在哪个货架。到了超市你直接拿,不用满超市找。

配置预下载:

pages.jsonpreloadRule 节点配置:

{
"preloadRule": {
"pages/index/index": {  // 触发预下载的页面
  "network": "all",     // 在什么网络下预下载:all=所有网络,wifi=仅WiFi
  "packages": ["pages-subpack/goods"]  // 预下载哪个分包
}
}
}

代码解释:
- pages/index/index:当用户访问首页时
- packages:自动下载这个分包


🔥 实战 35 分钟:3 个递进的小项目

项目 1:基础分包配置(5 分钟)

目标: 把一个商品列表页面拆成分包,理解最基础的分包结构。

完整代码:

项目结构:
├── pages/
│   └── index/
│       └── index.vue
├── pages-subpack/
│   └── product/
│       └── list.vue
├── manifest.json
└── pages.json

manifest.json:

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

pages.json:

{
"pages": [
{
  "path": "pages/index/index",
  "style": {
    "navigationBarTitleText": "首页"
  }
}
],
"subPackages": [
{
  "root": "pages-subpack/product",
  "pages": [
    {
      "path": "list",
      "style": {
        "navigationBarTitleText": "商品列表"
      }
    }
  ]
}
]
}

pages/index/index.vue:

<template>
<view class="container">
<text class="title">校园二手市场</text>
<button type="primary" @tap="goToProductList">浏览商品</button>
</view>
</template>

<script>
export default {
methods: {
goToProductList() {
  uni.navigateTo({
    url: '/pages-subpack/product/list'
  });
}
}
}
</script>

<style>
.container {
padding: 20px;
}
.title {
font-size: 24px;
display: block;
margin-bottom: 20px;
}
</style>

pages-subpack/product/list.vue:

<template>
<view class="container">
<text class="title">商品列表(分包页面)</text>
<view v-for="item in products" :key="item.id" class="product-item">
  <text>{{ item.name }} - ¥{{ item.price }}</text>
</view>
</view>
</template>

<script>
export default {

data() {
return {
  products: [
    { id: 1, name: '二手自行车', price: 200 },
    { id: 2, name: '教科书', price: 30 },
    { id: 3, name: '台灯', price: 50 }
  ]
}
}
}
</script>

<style>
.container {
padding: 20px;
}
.title {
font-size: 18px;
display: block;
margin-bottom: 15px;
}
.product-item {
padding: 10px;
border-bottom: 1px solid #eee;
}
</style>

预期输出:
点击"浏览商品"按钮 → 跳转到商品列表页(显示 3 个商品)

一句话解释: uni.navigateTo 跳转到分包页面时,微信会自动先加载该分包。


项目 2:带预下载的商品列表(15 分钟)

目标: 从本地 JSON 文件读取商品数据,并配置分包预下载。

需求:
1. 商品数据放在 static/data/products.json
2. 首页访问时预加载商品分包
3. 点击按钮展示商品列表

static/data/products.json:

[
{ "id": 1, "name": "二手自行车", "price": 200, "seller": "小明" },
{ "id": 2, "name": "高等数学教科书", "price": 30, "seller": "小红" },
{ "id": 3, name": "小米台灯", "price": 50, "seller": "小刚" },
{ "id": 4, "name": "AirPods Pro", "price": 600, "seller": "阿华" },
{ "id": 5, "name": "机械键盘", "price": 250, "seller": "阿杰" }
]

pages.json(加入预下载配置):

{
"pages": [
{
  "path": "pages/index/index",
  "style": {
    "navigationBarTitleText": "首页"
  }
}
],
"subPackages": [
{
  "root": "pages-subpack/product",
  "pages": [
    {
      "path": "list",
      "style": {
        "navigationBarTitleText": "商品列表"
      }
    }
  ]
}
],
"preloadRule": {
"pages/index/index": {
  "network": "all",
  "packages": ["pages-subpack/product"]
}
}
}

pages-subpack/product/list.vue(读取 JSON 数据):

<template>
<view class="container">
<text class="title">商品列表(从JSON加载)</text>
<view v-for="item in products" :key="item.id" class="product-item">
  <text class="product-name">{{ item.name }}</text>
  <text class="product-price">¥{{ item.price }}</text>
  <text class="product-seller">卖家:{{ item.seller }}</text>
</view>
</view>
</template>

<script>
export default {
data() {
return {
  products: []
}
},
onLoad() {
this.loadProducts();
},
methods: {
loadProducts() {
  // 读取静态 JSON 文件
  uni.request({
    url: '/static/data/products.json',
    success: (res) => {
      this.products = res.data;
    },
    fail: () => {
      console.error('加载商品数据失败');
    }
  });
}
}
}
</script>

<style>
.container {
padding: 20px;
}
.title {
font-size: 18px;
display: block;
margin-bottom: 15px;
}
.product-item {
padding: 15px;
border-bottom: 1px solid #eee;
display: flex;
flex-direction: column;
}
.product-name {
font-size: 16px;
font-weight: bold;
}
.product-price {
color: #ff6000;
margin-top: 5px;
}
.product-seller {
color: #888;
font-size: 12px;
margin-top: 5px;
}
</style>

预期输出:
进入商品列表页后,页面展示 5 个商品,每个显示名称、价格、卖家。

一句话解释: uni.request 从本地 static 目录读取 JSON 数据,加载到页面渲染。


项目 3:分类浏览 + 预下载(15 分钟)

目标: 实现一个分类浏览工具,用户点击不同分类只加载该分类的商品。

需求:
1. 首页显示"数码产品""图书""生活用品"3 个分类按钮
2. 每个分类是一个独立分包
3. 点击分类时预下载对应分包

新增 pages-subpack/digital/list.vue:

<template>
<view class="container">
<text class="title">数码产品</text>
<view v-for="item in products" :key="item.id" class="product-item">
  <text>{{ item.name }} - ¥{{ item.price }}</text>
</view>
<button @tap="goBack">返回首页</button>
</view>
</template>

<script>
export default {
data() {
return {
  products: [
    { id: 1, name: 'AirPods Pro', price: 600 },
    { id: 2, name: '机械键盘', price: 250 },
    { id: 3, name: '小米手环', price: 150 }
  ]
}
},
methods: {
goBack() {
  uni.navigateBack();
}
}
}
</script>

<style>
.container { padding: 20px; }
.title { font-size: 18px; display: block; margin-bottom: 15px; }
.product-item { padding: 10px; border-bottom: 1px solid #eee; }
</style>

pages/index/index.vue(分类选择):

<template>
<view class="container">
<text class="title">校园二手市场</text>
<text class="subtitle">选择分类浏览</text>
<button @tap="goCategory('digital')">📱 数码产品</button>
<button @tap="goCategory('book')">📚 图书</button>
<button @tap="goCategory('life')">🏠 生活用品</button>
</view>
</template>

<script>
export default {
methods: {
goCategory(category) {
  if (category === 'digital') {
    uni.navigateTo({ url: '/pages-subpack/digital/list' });
  } else if (category === 'book') {
    uni.navigateTo({ url: '/pages-subpack/book/list' });
  } else if (category === 'life') {
    uni.navigateTo({ url: '/pages-subpack/life/list' });
  }
}
}
}
</script>

<style>
.container { padding: 20px; text-align: center; }
.title { font-size: 24px; display: block; margin-bottom: 10px; }
.subtitle { font-size: 14px; color: #888; display: block; margin-bottom: 30px; }
button { margin: 10px 0; }
</style>

pages.json(配置多个分包 + 预下载):

{
"pages": [
{
  "path": "pages/index/index",
  "style": { "navigationBarTitleText": "首页" }
}
],
"subPackages": [
{
  "root": "pages-subpack/digital",
  "pages": [{ "path": "list", "style": { "navigationBarTitleText": "数码产品" } }]
},
{
  "root": "pages-subpack/book",
  "pages": [{ "path": "list", "style": { "navigationBarTitleText": "图书" } }]
},
{
  "root": "pages-subpack/life",
  "pages": [{ "path": "list", "style": { "navigationBarTitleText": "生活用品" } }]
}
],
"preloadRule": {
"pages/index/index": {
  "network": "wifi",
  "packages": ["pages-subpack/digital", "pages-subpack/book", "pages-subpack/life"]
}
}
}

预期输出:
首页点击"📱 数码产品" → 跳转到数码列表页,显示 AirPods Pro、机械键盘、小米手环。

一句话解释: 多个分包可以并行预下载,用户 WiFi 环境下打开首页时,后台静默下载所有分类分包。


💪 进阶 20 分钟:常见坑 + 性能小贴士

❌ 坑 1:路径写错了,页面打不开

错误示例:

{
"root": "pages-subpack/goods",  // 注意不是 pages-subpack-goods
"path": "list"  // 实际文件是 list.vue,这里只写文件名不加后缀
}

正确示例:

{
"root": "pages-subpack/goods",
"pages": [
{ "path": "list" }
]
}

注意! root 路径最后不要加 /path 只写文件名不含 .vue


❌ 坑 2:主包页面引用分包资源

错误示例(主包直接 import 分包里的文件):

// ❌ 主包的 pages/index/index.vue
import goodsList from '@/pages-subpack/goods/list.vue'  // 不允许!

正确做法: 主包和分包之间不能直接引用,只能用 uni.navigateTo 跳转页面。


❌ 坑 3:独立分包用了主包的公共资源

错误示例:
独立分包里用了 common/style.css(主包的公共样式),独立分包加载时会找不到。

正确做法: 独立分包如果要样式,把样式文件放独立分包自己的目录里。


❌ 坑 4:预下载分包数量超限

微信限制一次预下载不能超过 3 个分包。

错误示例:

"packages": ["分包A", "分包B", "分包C", "分包D"]  // ❌ 超过3个

正确做法: 只预下载用户最可能访问的 2-3 个分包。


❌ 坑 5:分包页面用 getApp() 获取全局状态

错误示例:

// 分包页面里
const app = getApp();
app.globalData.userInfo  // ❌ 独立分包里可能拿不到

正确做法: 分包页面用 uni.getStorageSync('userInfo') 或在 onLoad 里通过参数传递。


✅ 性能小优化:分包懒加载

默认分包是懒加载(用到才下载),但可以显式配置:

"preloadRule": {
"pages/index/index": {
"network": "wifi",
"packages": ["pages-subpack/goods"]  // 实际上这行已经足够了
}
}

如果想更激进地优化,用 uni.preloadSubPackage API 在特定时机预加载:

// 当用户停留在首页超过 3 秒时,偷偷预加载
setTimeout(() => {
uni.preloadSubPackage({
name: 'goods',
success: () => console.log('商品分包预加载成功')
});
}, 3000);

🔍 调试技巧:查看分包加载信息

在微信开发者工具里,打开「详情」→「项目配置」→「调试器」:

输入以下命令查看分包信息:

wx.getSubpackageInfo()  // 查看已加载的分包

或者在控制台查看 Network 面板,找 __WEGAME_SUBPACKAGE__ 开头的请求,就是分包加载的请求。


✏️ 练习题 + 作业题

练习题(10 分钟)

练习 1(2 分钟):修改分包路径
- 输入:把 pages-subpack/goods/list 改成 pages-subpack/product/list
- 预期输出:pages.json 配置正确,路径能正常跳转
- 提示:修改 root 字段和实际文件夹名称

练习 2(2 分钟):添加一个新分类
- 输入:在项目 3 基础上新增"服装"分类分包
- 预期输出:首页点击"👕 服装"能跳转到服装列表
- 提示:需要新增文件夹、配置 pages.json、新增按钮

练习 3(2 分钟):修改预下载网络条件
- 输入:把预下载从 wifi 改成 all
- 预期输出:手机流量环境下也能预下载
- 提示:只改 preloadRule 里的 network 字段

练习 4(2 分钟):给独立分包加配置
- 输入:把练习 2 的服装分包改成独立分包
- 预期输出:配置里多了 "independent": true
- 提示:在该分包的配置对象里加一个 independent 字段

练习 5(2 分钟):找错
- 输入:以下配置哪里有问题?

{
"root": "pages-subpack/goods",
"pages": [
{ "path": "list.vue" }
]
}
  • 预期输出:指出 path 不应该加 .vue 后缀
  • 提示:path 只写文件名

作业题(30 分钟-2 小时)

作业:做一个「校园服务小程序分包版」

  • 需求描述:做一个校园服务合集小程序,包含多个互不相关的功能
  • 功能点:
    1. 主包:首页,显示功能入口
    2. 分包 A:快递查询(输入单号查询物流)
    3. 分包 B:课表查询(显示周一到周五的课程)
    4. 分包 C:校园公告(列表 + 点击查看详情)
  • 加分项:
    1. 配置快递分包的预下载
    2. 公告列表页用 v-for 循环渲染
  • 验收标准:
  • 三个分包都能正常跳转
  • 每个分包有真实数据渲染(非空列表)
  • 代码有适当注释说明分包结构
  • 提交方式:评论区贴关键配置或 GitHub 链接

📚 总结 + 资源

本文学到的 3 个核心点:
1. 分包就是把小程序拆成"主包 + 多个功能包",按需加载
2. pages.jsonsubPackages 配置是分包的入口
3. preloadRule 可以让用户还没点之前就偷偷下载分包

延伸学习资源:
- uniapp 分包文档 - 官方配置参考
- 微信小程序分包加载指南 - 微信官方说明
- uniapp 性能优化实战 - 进阶优化技巧

互动钩子:
你在项目里用过分包吗?有没有遇到过审核被拒的情况?评论区聊聊,老粉优先回复!


📌 下章预告:学会了分包加载,下一章我们要解决"怎么让更多人用到你的小程序"——第5章 5.3 小程序发布流程,从开发到上架,一步步教你把作品发布到微信!

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。