先说结论:Next.js 16的Turbopack确实香,启动速度快得离谱,但当你用它构建生产包的时候,某些场景下会出现一些匪夷所思的问题。尤其是涉及到CSS模块和npm包的exports字段时,你可能会怀疑人生。
上周公司决定把项目从Next.js 15升级到16,原因很简单——Turbopack稳定版发布,官方号称生产构建速度提升2-5倍。技术选型会上大家都很兴奋,毕竟每次next build等半天确实折磨人。升级过程本身倒是一路畅通,npx @next/codemod@latest upgrade latest跑完,几个deprecated警告改掉,TypeScript类型检查也过了。本地next dev跑起来确实快,连HMR都快了不止一个档次。
然而,当我们把代码推到CI准备构建Docker镜像的时候,噩梦开始了。
那个CSS包死活导入不进去
问题出在一个很常见的场景——我们用shadcn/ui的新版动画方案,需要安装tw-animate-css这个包。官方文档写得很简单:
npm install tw-animate-css
然后在globals.css里加一行:
@import "tw-animate-css";
本地开发完全没问题,next dev跑得飞起。但next build的时候直接炸了:
Error: Turbopack build failed with 1 errors:
./src/app/globals.css:2:2
Module not found: Can't resolve 'tw-animate-css'
我当时第一反应是依赖没装好。检查了package.json,确实在dependencies里。进Docker容器看node_modules,文件也确实躺在node_modules/tw-animate-css/dist/tw-animate.css。但Turbopack就是报找不到。
排查了一圈发现一个诡异的现象:本地next dev正常,只有生产构建才出问题。这意味着Dev和Build用的不是同一套解析逻辑。查了官方文档才知道,Turbopack在生产构建时会严格遵循Node.js的exports字段规范,而tw-animate-css的package.json里是这么写的:
{
"name": "tw-animate-css",
"exports": {
".": {
"style": "./dist/tw-animate.css"
}
}
}
问题就在这——Turbopack在处理CSS的@import时,没有正确识别并解析npm包的exports.style字段。Webpack时代这是没问题的,因为Webpack会按自己的方式去找包的入口。但Turbopack更严格,它只认exports规范,偏偏对纯CSS包的style条件导出支持不完善。
禁用Turbopack?没那么简单
遇到这个问题,我第一反应是关掉它。官方文档提到过有个环境变量可以禁用Turbopack用于构建:
NEXT_DISABLE_TURBOPACK=1 next build
但当我把项目升级到Next.js 16.1.6后,发现这个变量对next build完全无效。日志依然傲慢地打印着▲ Next.js 16.1.6 (Turbopack),构建依然失败。查了一圈Issue,发现这确实是目前版本的一个限制——Node.js 20.9+是强制要求,Turbopack也是强制启用,没有什么优雅的降级方案。
说实话当时有点绝望。想着要不要先回退到Next.js 15,但项目已经用上了新的proxy.ts约定和一些16独有的特性,回退成本不低。
曲线救国:Docker构建时"偷"文件
最后我在Docker构建阶段找到了一个解法。既然Turbopack不认node_modules里的包exports字段,那我就把文件手动拷贝到源码目录,让构建工具去读相对路径。
第一步,改globals.css:
@import "tailwindcss";
@import "./tw-animate.css"; /* 从包名改为相对路径 */
第二步,在Dockerfile的构建阶段加一行拷贝指令:
# 阶段2: 项目构建
...
# 构建前,把node_modules里的css偷出来放到源码目录
RUN cp node_modules/tw-animate-css/dist/tw-animate.css ./src/app/tw-animate.css
RUN npm run build
这样做的几个好处:本地开发依然通过npm包管理,不用改任何源代码;Docker构建时动态拷贝,不污染Git仓库;而且确保使用的CSS版本与node_modules安装的版本100%一致。
构建终于成功了。
总结一下
Next.js 16的Turbopack确实是好东西,速度提升是实打实的。但如果你项目里用到了某些只提供exports.style字段的纯CSS npm包,在生产构建时可能会遇到类似的模块解析问题。目前的workaround就是手动把CSS文件拷贝到项目源码里,让Turbopack按相对路径去解析。
这个坑不算大,但排查过程挺折磨人的——本地Dev完全正常,一到CI就炸,非常容易让人怀疑是环境问题或者依赖问题。希望官方后续版本能完善对CSS包exports字段的支持吧。