返回博客

Next.js dev 下 CSS @import 链改了没效果?根因是 .next 缓存没失效

加了一个侧边栏折叠功能。JS 那侧改动——组件渲染、图标切换——HMR 秒到,一切正常。但匹配的 CSS:grid-template-columns 收窄、sidebar-rail 宽度、内容区 overflow: hidden——全部没有生效。三轮排查都指向「CSS 写错了/选择器优先级冲突/浏览器缓存」,但硬刷新、强制清缓存之后依然是老样子。直到用 curl 把编译产物拉下来一 grep,才发现那两条新规则根本不在编译后的 bundle 里。

现象

项目的 CSS 组织方式是 app/globals.css 里通过 @import 引入各功能分片:

/* globals.css */
@import './styles/06-workspace-chrome.css';

06-workspace-chrome.css 里新增了两个类:

.shell--collapsed {
  grid-template-columns: 40px minmax(0, 1fr);
}
.sidebar-rail {
  width: 40px;
}

重启 next dev,硬刷新浏览器——依然无效。DevTools 里对应元素找不到这两条规则,甚至在 Sources 面板翻遍所有 CSS chunk 也看不到它们。

根因

next dev 的增量编译对 JS 模块图做了精细的依赖追踪,但对 CSS @import 链的追踪是不完整的。具体来说:

  • 通过 JS import './x.css' 引入的样式:文件变动 → HMR 能感知到 → 重新编译。
  • 通过 CSS @import 从另一个 CSS 文件间接引入的样式:.next 缓存里已经有一份编译好的全局 CSS bundle,当@import 的那层文件发生变化时,缓存不会失效,重启 dev server 也不会强制重编译这条链路。

结果就是:旧规则(缓存里已有的)继续生效,新规则(缓存生成之后才写进去的)永远不会出现在 bundle 里——除非把缓存整个删掉。

坑:重启 dev server 不等于清缓存

next dev 重启时会复用 .next 目录里已编译的产物。对于通过 CSS @import 间接引入的文件,文件内容变了,缓存里的 bundle 却不会重新生成。这让「重启没用」成为一个强烈的误导信号——你会以为问题在 CSS 本身,而不是缓存。

排查:先确认是缓存问题,不是 CSS 写错了

不要靠 DevTools 猜——直接把编译产物拉下来验证:

# 1. 从页面 HTML 里取出实际的 CSS bundle 路径
CSS_PATH=$(curl -s http://localhost:3000/ \
  | grep -oE '/_next/static/css/[^"]+\.css' | head -1)
 
# 2. 把 bundle 下载到本地
curl -s "http://localhost:3000${CSS_PATH}" -o /tmp/out.css
 
# 3. 新规则是否编译进去了
grep -c "shell--collapsed" /tmp/out.css     # 0 = 没编译进去
 
# 4. 旧规则是否在(同一文件里)
grep -c "dossier-shell" /tmp/out.css        # >0 = 文件确实被 @import

如果旧规则存在、新规则缺失,且它们来自同一个文件——那就是缓存问题,和 CSS 语法无关。

正解

确认是缓存问题后,操作很简单:

# 杀掉 dev server、删缓存、重新启动
lsof -ti:3000 | xargs kill -9
rm -rf .next
npm run dev

启动完成(看到 Ready in)后,再跑一次 curl+grep 验证:

CSS_PATH=$(curl -s http://localhost:3000/ \
  | grep -oE '/_next/static/css/[^"]+\.css' | head -1)
curl -s "http://localhost:3000${CSS_PATH}" -o /tmp/out.css
grep -n -A 5 "shell--collapsed" /tmp/out.css

dev CSS 是未压缩的,能直接读到完整的声明块。确认规则在 bundle 里之后,浏览器硬刷新(Cmd+Shift+R)即可。

正解:rm -rf .next 然后重启

CSS 新增规则在 next dev 里不生效时,第一反应应该是清 .next 缓存,而不是找选择器优先级或浏览器缓存的问题。next build 每次都全量重编,生产环境不受影响——这是纯 dev-only 的陷阱。

补充:什么时候需要做这一步

记住这条心智模型:

  • JS 文件 / import './x.css' 方式引入的 CSS → HMR 有效,可以热更。
  • CSS 文件里通过 @import 间接引入的 CSS → 改了不一定能感知到.next 缓存可能包住旧版本。

当你发现「JS 改动生效了,配套的 CSS 没有」,第一步就是清缓存,别在 DevTools 里绕弯子。

一句话外卖

next dev 里,CSS @import 链上的文件改动不会让 .next 缓存失效;症状是新规则蒸发、老规则健在,rm -rf .next 重启是唯一正解。