uni-app在微信小程序国际化分包方案:优雅解决主包体积超限问题
一、背景与痛点
在开发大型 UniApp 项目时,国际化语言包往往是体积大户。随着业务发展,支持的语言种类增多、文案内容丰富,主包体积会快速膨胀,甚至触发微信小程序 2MB 主包限制。
核心问题
┌─────────────────────────────────────────────────────────────┐
│ 传统方案:主包包含所有语言 │
├─────────────────────────────────────────────────────────────┤
│ 主包 (2.1MB) │
│ ├── zh-CN.js (500KB) │
│ ├── en-US.js (500KB) │
│ ├── ja-JP.js (500KB) │
│ ├── ko-KR.js (500KB) │
│ └── 业务代码 (100KB) │
└─────────────────────────────────────────────────────────────┘
↓ 体积超限!微信审核不通过
二、解决方案:分包懒加载语言包
架构设计
┌─────────────────────────────────────────────────────────────┐
│ 优化后方案 │
├─────────────────────────────────────────────────────────────┤
│ 主包 (600KB) │
│ ├── zh-CN.js (500KB) ← 仅保留默认语言 │
│ └── 业务代码 (100KB) │
├─────────────────────────────────────────────────────────────┤
│ 分包 pagesData/ │
│ ├── i18n/lang/ │
│ │ ├── zh-CN.js │
│ │ └── en-US.js │
│ └── appDetails/ │
│ └── appDetails.vue ← 进入时自动加载分包语言包 │
└─────────────────────────────────────────────────────────────┘
↓ 主包体积大幅减少,通过审核
核心原理
- 主包仅包含默认语言:减少初始下载体积
- 分包语言包随页面懒加载:用户进入分包页面时才加载对应语言包
- 利用 mergeLocaleMessage 动态合并:Vue I18n 原生支持增量合并语言包
三、实现方案
3.1 主包入口:locale/index.js
import { createI18n } from "vue-i18n"
import zhCN from "./lang/zh-CN"
import enUS from "./lang/en-US"
// 创建基础 i18n 实例(仅包含核心语言)
const i18n = createI18n({
locale: uni.getStorageSync("lang") || "zh-CN",
fallbackLocale: "zh-CN",
legacy: false,
globalInjection: true,
messages: {
"zh-CN": zhCN,
"en-US": enUS
}
})
// 核心:动态合并分包语言包
const loadedModules = new Set()
export async function loadSubPackageI18n(pkgName) {
if (!pkgName) return
const locale = i18n.global.locale
const lang = typeof locale === 'string' ? locale : locale.value
const key = `${pkgName}-${lang}`
// 防止重复加载
if (loadedModules.has(key)) return
try {
// #ifdef H5 || MP-TOUTIAO || APP
// 其他平台直接动态导入
const messagesModule = await import(`../${pkgName}/i18n/lang/${lang}.js`)
const messages = messagesModule.default || messagesModule
i18n.global.mergeLocaleMessage(lang, messages)
loadedModules.add(key)
// #endif
// #ifdef MP-WEIXIN
// 微信小程序:主包无法加载分包,此处仅做标记,实际加载由分包完成
// #endif
} catch (err) {
console.warn(`[i18n] 加载分包语言失败:${pkgName}/${lang}`, err)
}
}
export default i18n
3.2 分包入口:pagesData/i18n/index.js
import { useI18n } from 'vue-i18n'
import zhCN from './lang/zh-CN.js'
import enUS from './lang/en-US.js'
/**
* 分包内语言包初始化函数
* 关键:在微信小程序环境下,必须在分包页面内部调用
*/
export function initPagesDataI18n() {
const { locale, mergeLocaleMessage } = useI18n()
const lang = typeof locale.value === 'string' ? locale.value : 'zh-CN'
// 根据当前语言选择对应语言包
const messages = lang === 'en-US' ? enUS : zhCN
// 合并到全局 i18n 实例
mergeLocaleMessage(lang, messages)
console.log(`[i18n] pagesData 分包语言包加载成功`)
}
3.3 分包页面调用:pagesData/appDetails/appDetails.vue
<script setup>
// #ifdef MP-WEIXIN
// 微信小程序必须在分包页面内加载语言包
import { initPagesDataI18n } from "../i18n/index.js"
initPagesDataI18n()
// #endif
import { onLoad } from "@dcloudio/uni-app"
onLoad(() => {
// 页面业务逻辑
})
</script>
四、关键技术点
4.1 微信小程序分包机制限制
| 限制类型 | 说明 | 解决方案 |
|---|---|---|
| 主包无法访问分包文件 |
import 分包文件会报 "找不到模块" 错误 |
在分包页面内自行加载 |
| 分包预加载 | 用户进入分包后才下载 | 利用 mergeLocaleMessage 动态合并 |
| 重复加载风险 | 多次进入同一分包页面 | 使用 Set 记录已加载模块 |
4.2 多端兼容策略
// #ifdef MP-WEIXIN
// 微信:在分包页面内调用 initPagesDataI18n()
// #endif
// #ifdef H5 || MP-TOUTIAO || APP
// 其他平台:主包动态 import 分包语言包
import(`../${pkgName}/i18n/lang/${lang}.js`)
// #endif
// #ifdef APP-HARMONY
// 鸿蒙:使用 import.meta.glob 静态收集
const locales = import.meta.glob('../pages*/i18n/lang/*.js', { eager: true })
// #endif
4.3 语言包结构设计
pagesData/i18n/lang/
├── zh-CN.js
└── en-US.js
语言包内容示例:
// pagesData/i18n/lang/zh-CN.js
export default {
appDetails: {
title: "应用详情",
size: "应用大小",
version: "版本号",
updateTime: "更新时间"
}
}
五、使用流程
用户进入分包页面
↓
触发 onLoad 生命周期
↓
调用 initPagesDataI18n()
↓
获取当前语言 locale
↓
加载对应语言包 zh-CN.js / en-US.js
↓
mergeLocaleMessage 合并到全局
↓
页面使用 t("appDetails.title") 即可
六、效果对比
| 指标 | 优化前 | 优化后 | 提升 |
|---|---|---|---|
| 主包体积 | 2.1MB | 600KB | -71% |
| 首屏加载时间 | 3.2s | 1.8s | -44% |
| 审核通过率 | 失败 | 通过 | ✅ |
| 分包按需加载 | 否 | 是 | ✅ |
七、注意事项
- 语言包命名规范:保持与主包一致的语言标识(zh-CN、en-US)
-
避免重复键名:分包语言包建议使用独立命名空间(如
appDetails.*) - 错误处理:加载失败时应有降级方案(使用 fallbackLocale)
-
热更新兼容:分包更新后需清空
loadedModules缓存
八、总结
通过将语言包按分包拆分,我们成功解决了微信小程序主包体积超限问题。核心思路是:
- 主包瘦身:仅保留必要的默认语言
- 分包自治:每个分包管理自己的语言包
- 懒加载策略:用户进入时才加载对应语言包
-
动态合并:利用 Vue I18n 的
mergeLocaleMessage实现增量加载