tree shaking 多语言包必须在 js 模块层实现:语言资源需为 export 导出的 esm 模块,仅按需 import 对应语言文件,避免 json/内联 script 或硬编码 script 标签,第三方库须用 esm 版本并分离语言数据与逻辑。

HTML 本身不参与 Tree Shaking,多语言包的“摇树”必须发生在 JS 模块层——语言资源得是 export 导出的 ESM 对象,且只被实际用到的语言版本 import,否则全量打包照旧。
多语言资源必须写成 ESM 模块,不能放 JSON 或内联 script
把 zh.json 或 en.js 直接扔进 HTML 的 <script src="locales/zh.js"></script> 里,Rollup/Webpack 完全看不见它:没 import 就没依赖链,更谈不上 shaking。
- ✅ 正确做法:每个语言文件导出命名对象,如
locales/zh.js写export const messages = { login: '登录' }; - ✅ 入口 JS 中按需导入:
import { messages } from './locales/zh.js'(仅构建时用到的语言) - ❌ 错误示例:
<script type="module" src="locales/en.js"></script>—— 即使是 ESM,独立 script 标签也不进入模块图 - ⚠️ 注意:
export default { ... }不如export const messages = {...}可靠,某些 bundler 对 default export 的 shaking 更保守
HTML 模板里不能动态决定语言包路径
html-webpack-plugin 或 @rollup/plugin-html 插件只是把预设好的 JS 路径注入 HTML,它不会、也不能根据用户语言自动删掉其他语言包的 <script></script> 标签。
- ❌ 不要写:
<script src="locales/<%=%20lang%20%>.js"></script>—— 这是运行时拼接,构建期无法分析 - ✅ 正确路径:构建前就确定语言,比如通过 Webpack 的
DefinePlugin注入LANG='zh',再让入口 JS 用if (LANG === 'zh') import('./locales/zh.js') - ✅ 更稳妥:用
import()动态导入 +Promise.all加载当前语言所需的所有 chunk,确保只有该语言包进产物 - ⚠️ 风险点:如果模板里硬编码了
<script src="locales/de.js"></script>,哪怕代码没引用它,这个请求仍会发,且对应 JS 文件会被完整打包
第三方 i18n 库必须用 ESM 版本并按需引入
像 i18next 或 vue-i18n 这类库,如果直接 import i18n from 'i18next',整个库(含所有语言解析器、后端适配器)都会被打包进来,Tree Shaking 失效。
- ✅ 优先选
i18next-browser-languagedetector这类拆分包,再import { init } from 'i18next'(确认其package.json有"module"字段) - ✅ 语言数据必须和逻辑分离:不要在初始化函数里
import * as locales from './locales',改用import { zh } from './locales/zh' - ⚠️ 常见坑:
export * from './locales/en'会让所有导出都被保留,即使只用了en.login;应改为export { login, logout } from './locales/en' - ⚠️
sideEffects: false在项目package.json中必须设置,否则构建工具不敢删除空的 locale 文件(哪怕没 import)
真正起作用的不是 HTML 里的语言切换按钮,而是 JS 入口里那一行 import('./locales/' + lang + '.js') —— 它决定了模块图的边界,也决定了哪些字符串最终进 bundle。漏掉这个,再多的 HTML 多语言配置都只是表面功夫。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











