- Published on
解决 Next.js Build 时特定分类 API 分页失败的诡异 Bug
- Authors
- Name
- Tails Azimuth
问题背景
在开发学会官网的过程中,我遇到了一个极其诡异的问题:在 Next.js build 过程中,project-notice
(项目通知)分类的文章列表始终无法正常获取,而其他分类如 xuehui-news
(学会新闻)、science-column
(科普专栏)等都工作正常。
问题现象
- ✅ 本地开发环境:所有功能正常
- ✅ 直接 curl API 调用:返回正确数据
- ✅ 其他分类:build 时工作正常
- ❌ project-notice 在 build 时:返回空数据
问题调试过程
第一阶段:API 调用对比
首先,我创建了一个调试脚本来对比不同分类的 API 响应:
// scripts/debug-api-categories.js
async function testCategory(category, params = {}) {
const searchParams = new URLSearchParams()
searchParams.set('category', category)
Object.entries(params).forEach(([key, value]) => {
searchParams.set(key, value.toString())
})
const url = `http://localhost:3003/api/articles?${searchParams}`
console.log(`测试分类: ${category}`)
console.log(`URL: ${url}`)
const response = await fetch(url)
const data = await response.json()
console.log(`文章数: ${data.data?.articles?.length || 0}`)
}
调试结果发现:
# 正常的分类
📂 测试分类: xuehui-news
📊 API响应: { success: true, articlesCount: 8, totalCount: 169 } ✅
# 异常的分类
📂 测试分类: project-notice (带分页参数)
📊 API响应: { success: true, articlesCount: 0, totalCount: 0 } ❌
📂 测试分类: project-notice (不带分页参数)
📊 API响应: { success: true, articlesCount: 1, totalCount: 1 } ✅
关键发现:project-notice
在带分页参数时返回空数据,但不带分页参数时正常!
第二阶段:build 时日志分析
在 build 时的日志中发现了关键信息:
🔄 [getNotificationsData] 第 1 次尝试(带分页参数)
📊 API响应: { success: true, articlesCount: 0, totalCount: 0 } ❌
🔄 [getNotificationsData] 第 2 次尝试(仅category参数)
📊 API响应: { success: true, articlesCount: 1, totalCount: 1 } ✅
这说明问题不在于 API 服务本身,而在于特定参数组合下的请求处理。
第三阶段:深入后端代码分析
我查看了后端 API 服务器的实现(Express.js):
// scripts/wechat-api-server.js
app.get('/api/articles', (req, res) => {
const { category, page = 1, limit = 10, refresh } = req.query
let articles = scanner.getArticles(refresh === 'true')
// 按分类筛选
if (category) {
articles = articles.filter((article) => article.category === category)
}
// 分页
const pageNum = parseInt(page)
const limitNum = parseInt(limit)
const startIndex = (pageNum - 1) * limitNum
const endIndex = startIndex + limitNum
const paginatedArticles = articles.slice(startIndex, endIndex)
// ...
})
后端逻辑看起来完全正常,分页计算也没有问题。
第四阶段:网络请求对比
最关键的发现来自于对比 curl 请求和前端 fetch 请求:
# 直接 curl 请求 - 完全正常
curl "http://localhost:3003/api/articles?category=project-notice&page=1&limit=8"
# 返回:{"success":true,"data":{"articles":[...1篇文章...]}}
# 前端 build 时的 fetch 请求 - 返回空数据
fetch("http://localhost:3003/api/articles?category=project-notice&page=1&limit=8")
# 返回:{"success":true,"data":{"articles":[]}}
这说明问题出现在前端 fetch 请求的处理上!
解决方案
根本原因分析
经过深入分析,我发现问题出在网络请求的缓存机制上:
- build 时的并发请求可能触发了缓存冲突
- 前端 fetch 请求可能使用了错误的缓存数据
- curl 请求默认无缓存,所以始终正常
最终修复
在前端 wechatApi.ts
中添加了明确的请求头,特别是 Cache-Control
:
const response = await fetch(requestUrl, {
signal: AbortSignal.timeout(this.config.apiTimeout),
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
'User-Agent': 'NextJS-Build-Client',
'Cache-Control': 'no-cache', // 🔑 关键修复
},
})
辅助改进
- 改进了响应解析:
// 从直接 JSON 解析改为先获取文本再解析
const responseText = await response.text()
const result = JSON.parse(responseText)
- 添加了重试机制:
// 多种参数组合的重试逻辑
if (retryCount === 0) {
// 第一次:完整参数
result = await wechatApi.getArticles({
category: 'project-notice',
limit: ITEMS_PER_PAGE,
page: currentPage,
})
} else if (retryCount === 1) {
// 第二次:仅 category 参数
result = await wechatApi.getArticles({
category: 'project-notice',
})
}
- 增强了调试能力:
console.log(`📝 [wechatApi.getArticles] 原始响应:`, responseText.substring(0, 500))
console.log(`🔗 [wechatApi.getArticles] 响应头:`, Object.fromEntries(response.headers.entries()))
修复效果
修复后的 build 日志:
🔧 [getNotificationsData] 使用完整参数: category=project-notice, limit=8, page=1
📡 [wechatApi.getArticles] 响应状态: 200 OK
📊 [wechatApi.getArticles] API响应: { success: true, articlesCount: 1, totalCount: 1 } ✅
🎯 [getNotificationsData] 第 1 次尝试成功获取到文章,跳出重试循环
现在 project-notice
分页功能在 build 时完全正常工作!
经验总结
这个 Bug 的特殊性
- 环境特异性:只在 build 环境下出现,本地开发正常
- 分类特异性:只影响特定分类,其他分类正常
- 请求方式特异性:curl 请求正常,fetch 请求异常
- 参数特异性:带分页参数异常,不带分页参数正常
调试技巧
- 分层调试:从前端到后端,从网络到缓存,逐层排查
- 对比调试:正常功能 vs 异常功能,找出差异点
- 请求拦截:详细记录网络请求的完整信息
- 日志详化:在关键节点增加详细的调试日志
关键教训
- 缓存是双刃剑:能提升性能,也可能导致诡异问题
- 网络请求头很重要:
Cache-Control
等头部对行为有重大影响 - 环境差异需重视:开发环境正常不代表生产环境正常
- 重试机制是保险:为边界情况提供容错能力
这个 Bug 的解决过程展现了前端调试的复杂性,也提醒我们在处理网络请求时要格外注意缓存机制的影响。最终通过 Cache-Control: 'no-cache'
这个简单的修复解决了这个困扰已久的问题。