从点击“保存留言”到刷新后仍然存在
起点:用户只看到一个按钮
tsx
<button onClick={handleSubmit}>保存留言</button>用户点击以后,浏览器查找 handleSubmit 函数。此时请求还没有自动发生。
第一站:前端函数整理数据
ts
async function handleSubmit() {
const response = await fetch('/api/messages', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text })
})
}这段代码准备了四样东西:
text
地址 /api/messages
方法 POST
格式 application/json
内容 { "text": "你好" }第二站:浏览器真正发出 HTTP 请求
Network 中会出现一行:
text
Name messages
Method POST
Status Pending,随后变成状态码如果点击后 Network 没有这一行,问题还在前端。不要先查数据库。
第三站:后端路由匹配地址和方法
ts
app.post('/api/messages', async (req, res) => {
// 处理请求
})只有地址和方法都匹配,才会进入这个函数。
text
前端 POST /api/messages
后端只有 GET /api/messages
结果可能是 404 或 405同一个地址可以用 GET 读取、用 POST 新建。方法不是装饰。
第四站:后端检查身份和输入
后端可能依次检查:
- 用户是否登录。
- 用户是否有写入权限。
- 请求里有没有 text。
- text 是否为空或超过长度。
失败时应该尽早返回:
ts
return res.status(400).json({ error: '留言不能为空' })第五站:写入数据库
ts
const message = await db.messages.create({
text,
userId: currentUser.id
})数据库可能生成:
json
{
"id": 42,
"text": "你好",
"user_id": 7,
"created_at": "2026-08-26T03:10:00Z"
}id 用来唯一找到这条留言,created_at 记录创建时间。
第六站:后端返回 HTTP 响应
ts
return res.status(201).json(message)响应包含:
text
状态码 201,表示成功创建资源
Headers 说明响应格式等信息
Body 新留言 JSON第七站:前端读取响应
只调用 fetch 还不够。前端还要判断结果:
ts
if (!response.ok) {
const error = await response.json()
throw new Error(error.message || '保存失败')
}
const savedMessage = await response.json()fetch 遇到 400 或 500 时不一定自动进入 catch。检查 response.ok 很重要。
第八站:更新页面 state
ts
setMessages((current) => [...current, savedMessage])React 看到 state 变化后重新渲染列表,新留言出现在页面。
如果 Network 返回 201、数据库也有记录,但页面不更新,重点查这一站。
为什么刷新后还能看到
刷新会清空当前页面的 React state。页面重新打开时要再请求:
text
GET /api/messages
↓
后端从数据库 SELECT
↓
返回留言数组
↓
前端 setMessages所以真正让留言长期存在的是数据库,不是 state。
一次请求的排错检查单
text
按钮函数运行了吗 Console
请求发出了吗 Network 列表
地址和方法正确吗 Headers
发送内容正确吗 Payload
状态码是什么 Headers
后端返回了什么 Response
后端内部为什么失败 服务器日志
数据库是否真的写入 数据库控制台
前端是否更新 state React 代码和 Console五种结果怎样判断
text
没有请求 前端事件或 JavaScript 先出错
400 发送数据不符合后端规则
401/403 登录或权限
500 后端内部错误,查服务器日志
200/201 请求成功,继续验证数据和页面更新POST 请求返回 201,为什么页面仍然可能没有出现新留言?
答案:201 证明后端成功创建了资源,但前端还需要读取 Response 并更新 state,列表才会重新渲染。