最近维护老项目,用的 Vite + AntD5,本地开发跑着稳稳当当,一到CI打包直接炸锅,报错就是找不到 rc-picker/es/generate/dayjs。
一开始瞎忙活,装依赖、删node_modules、清缓存全套操作试遍,半点用没有。沉下心捋清楚构建流程、解析规则差异,才算彻底搞懂为啥会出这个问题,顺便把大家最容易混淆的Node解析、Rollup解析分清。
问题现状
本地开发毫无异常,仅执行打包命令就必报错,单纯缺失依赖的可能性直接排除,本质就是构建工具和组件库写法不兼容。
网上通用解法基本都是误区
- 手动安装dayjs、rc-picker:装完依旧报错
- 重装所有依赖、清空锁文件:问题纹丝不动
- 修改打包体积警告配置:只能消提示,解决不了核心报错
说到底,这根本不是缺包,就是写法和构建解析规则对上了。
核心根源讲透
1. AntD5 写法本身就不标准
AntD5里日历、日期选择器这类组件,内部直接写死导入路径:
js
import "rc-picker/es/generate/dayjs"
这种写法属于直接引用第三方包内部私有文件,不是行业通用的标准引入方式。
正常合规引入只有两种:相对路径引入、直接写包名引入。第三方包内部目录、子文件都属于私有实现,官方随时能改动,根本不对外暴露,硬写路径本身就留有隐患。
2. 重点厘清:Rollup 到底遵循什么规范
答案:Rollup 严格遵循原生 ESM 导入语法 + 官方模块导出规范 它认的导入逻辑很死板:
- 只认裸模块名导入:
import xxx from 'dayjs' - 只认相对/绝对文件路径导入
- 不认「包名+深层私有子目录」这种跨包内部硬路径
原因很简单:标准 ESM 设计里,一个包能被外部引入的内容,必须在 package.json 的 exports 字段显式声明暴露出来。 rc-picker 根本没把 es/generate/dayjs 这个路径对外导出,按照 ESM 标准,外部就不该直接引入,Rollup 自然直接拦截报错。
3. 彻底分清:Node解析 和 Rollup解析 谁背锅
-
Node 原生解析 偏向运行时兼容,规则宽松,属于"能找到就放行",就算没声明导出、写深层路径,本地运行大多能正常识别,所以本地开发不会报错。它不严格卡死 ESM 导出约束,容错拉满。
-
Webpack 解析 属于过度兼容,直接无视 ESM 导出约束,暴力遍历遍历 node_modules 目录找文件,不管规不规范都能匹配到,所以老项目用它打包从来不出这个错。
-
Rollup 解析(打包报错真正原因) Vite 打包阶段全权交给 Rollup,它死守标准 ESM 语法和模块导出规则,不做多余兼容。 没在 package.json 对外暴露的内部文件路径,一律判定为非法引入,直接抛出解析失败错误,这就是打包必崩的核心。
简单直白总结: 本地跑得通,是Node运行时宽松兼容 ;打包直接报错,是Rollup严格执行ESM标准,不纵容投机取巧的野路子写法。
而 AntD6 直接彻底解决了这个问题,换掉了老旧的rc-picker,改用规范的@rc-component/picker,内部全部改成标准包名引入,同时完善了包导出配置,完全贴合 ESM 规范,所以升级之后直接自愈,不用改任何配置。
4. Vite 预构建加剧了这个问题
Vite为了提升启动速度,用esbuild做预构建,它只会扫描我们业务代码直接引入的依赖,只会把antd整体预编译,不会深入解析antd内部的所有import语句。
这就导致antd内部藏着的违规路径,Vite预构建阶段完全感知不到,不会提前处理编译。等到后面Rollup正式打包时,冷不丁碰到这个未处理的违规路径,自然直接解析失败终止打包。
完整报错流程
- 业务代码引入antd日期组件
- Vite预编译antd整体代码,内部违规导入语句原封不动保留
- 预构建完成,进入Rollup打包流程
- Rollup按照ESM标准校验,识别到未暴露的深层私有路径,拒绝解析
- 模块匹配失败,打包直接终止
为啥其他第三方库从来不会出现这个问题
市面上绝大多数成熟第三方库,内部依赖全部用标准包名引入,不会写死内部文件路径,同时都规范配置了模块导出字段。 这种写法完全贴合 ESM 语法,不管是Vite预构建,还是Rollup打包解析,都能正常识别,自然不会出现解析失败的情况,唯独老版本antd踩了这个坑。
无需升级AntD6,最简解决方案
不想大版本升级组件库,不用装任何额外插件,只需要在vite配置里,手动告诉Vite提前编译这个扫描不到的模块即可:
js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
// 主动强制预构建深层私有模块
optimizeDeps: {
include: [
"rc-picker/es/generate/dayjs"
]
},
build: {
// 消除打包体积过大警告
chunkSizeWarningLimit: 3000
}
})
修改配置后清空缓存重新打包,问题直接解决。
不同Node版本有没有影响?会不会爆出不一样的错误?
-
核心结论 这个打包解析失败的根本错误,和Node版本无关,根源始终是Rollup恪守ESM标准,拒绝非法路径引入,不管高低版本Node,Vite+AntD5组合大概率都会触发这个问题。
-
版本差异带来的区别
- 低版本Node(16及以下):运行时解析容错性高,只会单纯爆出路径找不到这一个错误,问题单一好定位
- 高版本Node(17及以上):Node自身也开始收紧 ESM 规范,强化
exports导出校验,会和Rollup规则叠加,额外爆出路径未导出、模块无对外暴露等新报错,问题变得更复杂
- 运行与构建解析区分
- 本地开发调试:由Vite接管模块解析,几乎不受Node版本影响
- 线上打包构建:Rollup为主执行ESM标准校验,Node环境规则为辅,高版本Node会放大不规范写法的报错问题
- 通用适配性 上面给出的vite配置方案,兼容所有主流Node版本,不管项目用哪个版本运行,都能稳定解决打包报错。
最后总结
- 报错根源不是缺失依赖,是AntD5硬写私有深层路径,和Rollup遵循的标准ESM导入/导出规范产生冲突
- 明确划分:打包报错看Rollup的ESM标准,而非Node宽松的运行时解析规则
- Webpack靠无底线兼容兜底,Vite+Rollup靠原生ESM语法约束代码规范性,这也是新旧构建工具最大的差异
- 长远最优方案是升级AntD6贴合规范,临时项目应急,直接配置optimizeDeps强制预构建即可完美解决程