阅读视图

发现新文章,点击刷新页面。

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  ← 进入时自动加载分包语言包          │
└─────────────────────────────────────────────────────────────┘
    ↓ 主包体积大幅减少,通过审核

核心原理

  1. 主包仅包含默认语言:减少初始下载体积
  2. 分包语言包随页面懒加载:用户进入分包页面时才加载对应语言包
  3. 利用 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%
审核通过率 失败 通过
分包按需加载

七、注意事项

  1. 语言包命名规范:保持与主包一致的语言标识(zh-CN、en-US)
  2. 避免重复键名:分包语言包建议使用独立命名空间(如 appDetails.*
  3. 错误处理:加载失败时应有降级方案(使用 fallbackLocale)
  4. 热更新兼容:分包更新后需清空 loadedModules 缓存

八、总结

通过将语言包按分包拆分,我们成功解决了微信小程序主包体积超限问题。核心思路是:

  1. 主包瘦身:仅保留必要的默认语言
  2. 分包自治:每个分包管理自己的语言包
  3. 懒加载策略:用户进入时才加载对应语言包
  4. 动态合并:利用 Vue I18n 的 mergeLocaleMessage 实现增量加载
❌