必须写自定义 webpack 插件,在 resolveid 钩子中匹配 locale 路径并重定向至空模块,再于 load 钩子返回 'export default {};',否则 tree-shaking 无法剔除未用语言包。

能移除,但不能靠 Webpack 自带功能,必须写自定义插件拦截 resolveId 阶段,对 /locale/xxx.js 类路径做白名单过滤。直接配 TerserPlugin 或开启 tree-shaking 没用——语言包是按文件加载的,不是按导出项引用的。
为什么 tree-shaking 对 locale 文件完全无效
Tree-shaking 依赖 ESM 静态分析导出与导入关系,而语言包通常以独立模块形式被显式引入:import 'dayjs/locale/zh-cn' 或 require('ant-design-vue/lib/locale-provider/zh_CN')。Webpack 把每个 locale 文件当作一个“有副作用”的完整模块,只要被 require/import 就保留,不会分析内部是否真被使用。
- 即使你只用了
zh-CN,import 'dayjs/locale/*'全量写法会让所有 locale 被打包 -
sideEffects: false在 package.json 里对第三方 locale 目录不生效(你改不了人家的配置) - 动态
require()或字符串拼接路径(如require(`./locale/${lang}`))会彻底关闭静态分析
核心思路:在 resolveId 钩子中重定向非白名单 locale
Webpack 插件的 resolveId 是模块路径解析的入口,此时还能干预“这个路径最终加载哪个文件”。我们在这里匹配 locale 路径,把非白名单语言包重定向到一个空模块,让后续构建流程“加载了但什么也没做”。
- 匹配正则建议用:
/[\/](locale|locales|lang|language)[\/][a-z_-]+.js$/i,覆盖moment/locale、element-plus/lib/locale等常见结构 - 白名单语言要统一转小写比对,避免
ZH-CN和zh-cn被判为不同 - 返回
{ id: ' virtual:empty-locale', external: true }是关键:
