如果做国际化,但是国际化的页面有100多个页面,运用的还是vue的国际化插件,把一些资源又放在服务器,如何优化这个国际化操作,使得页面打开不会加载过慢

26 阅读2分钟
  1. 语言包全部走 HTTP 缓存 + CDN(Immutable + Brotli),不随业务 JS 一起打包
  2. “语言维度” + “页面维度” 拆包 → 每个用户最多下载 n(语言) × m(页面) 份 JSON,且可并行预取
  3. 首屏只拉 1 份「当前语言 + 当前页面」最小 JSON(≈3–8 kB),其他资源懒加载或闲时预加载
  4. 本地 IndexedDB 持久化,二次进入 0 请求
  5. 构建阶段做 key 去重 + 压缩 + 雪碧合并,降低总流量 30–50%

二、目录与构建约定

public/lang/               // CDN 实际目录
 ├─ zh-CN/
 │   ├─ common.json       // 全局高频词条(按钮、提示)
 │   ├─ page-home.json
 │   ├─ page-about.json
 │   └─ …
 ├─ en-US/
 │   └─ …(同构)

构建脚本(webpack/vite 插件)自动完成:

  • 提取各页面 this.$t('xxx') 出现的 key → 生成单页 JSON
  • 所有页面都出现的 key → 抽进 common.json
  • 对 JSON 做 lz-string 压缩 → 体积再降 15–25%
  • 生成 lang-manifest.json(哈希表),供运行期做「差量更新」

三、运行期架构

  1. 创建 i18n 实例(空 messages)
export const i18n = createI18n({
  legacy: false,
  locale: getPreferredLocale(), // 用户上次选择或浏览器语言
  fallbackLocale: 'en-US',
  messages: {}
})
  1. 通用加载器
const manifest = await fetch('/lang/lang-manifest.json').then(r => r.json())
const cache = await openDB('i18n', 1, { upgrade: db => db.createObjectStore('lang') })

export async function loadLocaleBundle(locale: string, page: string) {
  const bundleId = `${locale}/${page}`                       // 如 zh-CN/page-home
  // 1.  indexedDB 命中则直接返回
  const hit = await cache.get('lang', bundleId)
  if (hit) return hit

  // 2. 否则 CDN 拉取(带 etag + br)
  const url = `/lang/${bundleId}.json`
  const json = await fetch(url).then(r => r.json())

  // 3. 写入缓存 & 注册到 i18n
  await cache.put('lang', bundleId, json)
  i18n.global.setLocaleMessage(locale, {
    ...i18n.global.getLocaleMessage(locale),
    ...json
  })
  return json
}
  1. 路由级自动加载
router.beforeEach(async (to, from, next) => {
  const locale = i18n.global.locale.value
  const page = to.name as string                    // 路由 name 与 JSON 文件名保持一致
  await loadLocaleBundle(locale, page)              // 耗时 2060 ms(CDN)
  next()
})
  1. 语言切换
async function changeLanguage(locale: string) {
  // 先拉 common,再拉当前页面
  await loadLocaleBundle(locale, 'common')
  await loadLocaleBundle(locale, router.currentRoute.value.name as string)
  i18n.global.locale.value = locale
  localStorage.setItem('locale', locale)
}

四、性能细节

  1. 首屏
  • 只多 1 个 JSON 请求(common + 当前页面已合并)→ 加载体积 <10 kB
  • 与业务 JS 并行,不阻塞渲染
  • 服务器开启 br + cache-control: max-age=31536000, immutable
  1. 后续页面
  • 路由跳转前 20 ms 触发加载,用户无感知;失败时回退到 key 本身(兜底)
  1. 二次访问
  • IndexedDB 命中 → 0 网络请求,直接解析 JSON(<5 ms)
  1. 预加载策略(可选)
  • 利用 requestIdleCallback 在浏览器空闲时把「同语言其余页面」拉回来 → 后续跳转 0 延迟
  • PWA 场景可配 workbox 做后台同步

五、常见坑 & 解决

  1. 打包时动态 import() 会把所有语言目录打进去 → 改用 纯运行时 fetch,不用 import()
  2. 页面 key 重复率高 → 构建阶段做 字典去重 + 缩写,可把 400 kB 原始文本压到 120 kB
  3. 服务端渲染(SSR) → 在 serverPrefetch 里同步拉取对应 JSON,直出带翻译的 HTML,防止水合闪烁
  4. 右侧-to-Left 语言 → 在 changeLanguage 里同步设置 document.dir='rtl',并动态加载 rtl 样式