首页›分类文章›计算机技术›web 开发›Next.js 构建缓存的幽灵错误

Next.js 构建缓存的幽灵错误

计算机技术 · web 开发2026-09-195次浏览0 个评论

本篇是《手写一个内容型技术博客站》系列第 3 篇。

调试内容站最容易抓狂的不是业务 bug,而是"环境问题伪装成代码问题"。这篇记录三个真实踩过的坑,每一个都曾让我对着"看起来是对的"代码怀疑人生。

一、Turbopack 持久缓存重放旧失败

症状

npm run build 报:

text
Can't resolve '@tailwindcss/typography' in '<project>/src/app'
CssSyntaxError in globals.css:1:1

于是按套路排查:

  • package.json / package-lock.json / node_modules 全部验证无误
  • require.resolve('@tailwindcss/typography') 成功
  • 删除 lock + node_modules 重装 npm ci 后报错一字不变

诡异到极点:环境验证全对,build 却还报错。

根因

第一次 build 失败时,Turbopack 把"无法解析模块"的失败结果写进了 .next/build 持久缓存。之后每次 build 命中该陈旧缓存,重放同样的失败——报错栈里的 turbopack:///[turbopack-node]/... 就是缓存重放产物。

rm -rf node_modules 清不掉它,因为缓存根本不在 node_modules 里。

修法与铁律

text
rm -rf .next && npm run build

铁律:环境报"依赖缺失/解析失败"但磁盘全验证无误时,先清 .next 再怀疑配置。 生产构建一律 rm -rf .next && npm run build,不要依赖 Turbopack 增量缓存,否则会重放旧失败。

附带坑:有人删 package-lock.json 重装,会让 tailwindcss 系列版本漂移(^4 解析到 3.x 时 @plugin 语法不受支持)。锁 v4 后重建 lock 前先确认 tailwindcss/@tailwindcss/postcss 是 4.x。

二、孤儿进程占端口:EADDRINUSE 假回归

症状

"我明明改了源码,重跑 build 后 next start,页面还是旧样式"。或者"dev 端口老是被占,新 server 起不来"。

根因

next start / next dev 的守护链是 npm exec → sh -c 'next' → next-server。只杀了外层 npm/next 父进程,next-server 子进程成了孤儿继续占着端口。新 server 因为 EADDRINUSE 起不来——但你以为它起来了,curl 打的其实是旧进程的旧产物。

排查顺序

text
# 1. 看端口被谁占
ss -tlnp | grep <port>

# 2. 列出 next 相关进程,看启动时间区分新旧
ps -eo pid,lstart,cmd | grep next-server

# 3. 只杀目标端口进程链(父+子都要)
# 用 ss 确认 owner PID,kill -9 孤儿 server PID
# 杀完二次确认端口已空
ss -tlnp | grep <port>

关键点:

  • 按端口定位 owner,别无差别 pkill -f "next"——会误杀别的实例,还会匹配到调用链自身(SIGKILL 自己)。
  • 起服后看日志确认 Ready in Xms 而非 EADDRINUSE,才算真起来。
  • 可能有多个 next-server 并存(不同时间起的,如 build 前起的旧 + build 后被挡下的新),用 ps -eo lstart 区分,全部杀净。

归因心法:别怀疑源码——先怀疑打到旧进程。 各种"我改对了怎么没生效",一半是这个。

三、CRLF 行尾:frontmatter 未剥离

症状

某篇文章详情页顶部出现:

text
---
title: xxx
slug: xxx

整个 frontmatter 被当成正文渲染出来了,标题、字段名全显示在页面顶部。

根因

processMarkdown 剥离 frontmatter 的正则:

js
/^---\n[\s\S]*?\n---\n?/

只匹配 LF(\n)行尾。如果文章是 Windows 环境下编辑保存成 CRLF(\r\n),正则匹配不到 ---\r\n,frontmatter 整段落进正文。

列表页走 matter()(gray-matter)不受影响,所以是"详情页正常、列表正常、偏偏详情页顶部多一段"的诡异形态。

修法

正则改成兼容 CRLF:

js
/^---\r?\n[\s\S]*?\r?\n---\r?\n?/

波及面:所有直接 readFileSync + 渲染的详情页(categories 详情、tutorials 内容页)都要同步改。列表页不受影响。

症状"正文顶部出现 --- 和字段名"先验文件行尾:

text
file -b <md>            # 看是 CRLF 还是 LF
xxd <md> | head         # 查 \r\n 字节

别一上来怀疑渲染管线。

四、排查顺序总结

这三类"环境伪装"放一起,给一个通用排查优先级:

  1. 先确认打到的是不是旧产物(端口/进程/缓存)——一半的"改错"是这个
  2. 环境验证与代码验证分开做——磁盘、node_modules、版本都对,仍有构建错 → 清缓存
  3. 内容文件先验"字节层"(行尾/NUL/编码),再怀疑渲染逻辑

环境问题的共同特征:错误日志指向的地方,看着都"对",但就是不过。这时候往"缓存、进程、编码"这三个"看不见"的地方想,往往一击即中。


给普通人的一句话:程序告诉你"这里错了",有时不是这里错了,是它看到了过期或错位的东西。

参考

  • Turbopack 缓存:https://nextjs.org/docs/app/guides/upgrading

评论

0

评论加载中…

发表评论

0/2000