6.1 前后端数据交互:Fetch 与跨域


6.1 前后端数据交互:Fetch 与跨域

本节摘要:此前页面与后端的交互靠表单加整页跳转,本节换成现代方式:JavaScript 用 Fetch 直接调第 5 章的 API,拿到 JSON 后只更新页面的局部——不闪屏、不跳转。顺带排掉开发期必踩的跨域坑:浏览器的同源策略是什么、CORS 放行怎么配。学完本节,轻记账的页面与数据彻底解耦,真正的前后端分离成形。

本节要解决什么

阅读完本节,你应当能够:

  1. 用 Fetch 发 GET 与 POST 请求,解析 JSON 响应
  2. 区分网络错误与业务错误,两种失败分别处理
  3. 解释同源策略保护的是什么,跨域为什么会发生
  4. 配置 CORS 放行,说明生产与开发环境的差异
  5. 用"渲染局部"的方式重做轻记账首页

表单整页跳转的三个代价

回想 4.3 的记账流程:提交表单 → 整页刷新 → 重定向回首页。能用,但代价明显:全页重载,网速慢时白屏半天;状态丢失,用户滚动位置、未提交的草稿全部归零;交互僵硬,想做个"输完金额实时显示合计"的动态效果,表单模式根本做不到。Fetch 的思路是让 JavaScript 在后台发请求、拿数据、改局部,页面本身不动——第 4 章学的"JS 三拍"(找元素、绑事件、改内容)在此补全最后一环:改内容的原料来自 API。

图 6-1 两种交互模式的对比

图 6-1 两种交互模式的对比

Fetch 基本式:GET 与 POST

// GET:读账目列表 async function loadRecords() { const resp = await fetch("/api/records?month=2026-09"); if (!resp.ok) { // 401、404、500 都会走到这 showMessage("加载失败,状态码 " + resp.status); return; } const records = await resp.json(); // JSON 文本解析成数组 renderList(records); // 改局部:重画列表 } // POST:记一笔 async function addRecord(item, amount) { const resp = await fetch("/api/records", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ item, amount }), }); if (resp.status === 201) { loadRecords(); // 成功后重新拉列表 } else if (resp.status === 400) { const err = await resp.json(); showMessage(err.error); // 用服务器的报错文案 } else { showMessage("网络或服务器异常"); } }

四个要点。async 与 await 是现代写法:网络请求要等,await 让代码读起来像同步,不嵌套回调。状态码要分层看:resp.ok 管"有没有成功",具体状态码管"为什么没成功"——网络断开抛异常走 catch,服务器报错走状态码分支,业务校验错(400)读响应体里的 error 字段。POST 要显式声明内容类型:服务器才知道怎么解析正文。成功后重拉列表:前端不做本地账本,永远以服务器数据为准,避免两份数据对不上。

跨域:同源策略与 CORS 放行

本地开发时你很可能撞上这样的报错:请求被浏览器的同源策略拦截。同源指协议、域名、端口三者完全一致;三者任一不同就是跨域,浏览器默认禁止页面里的脚本读取跨域响应——这是浏览器在保护用户:防止恶意网站拿着你的登录态(Cookie)偷偷访问你的银行页面。策略挡的是浏览器,curl 不受限制,这就是"接口明明是好的、页面里就是不通"的原因。

放行的方式是 CORS:服务器在响应头里声明"我允许谁跨域访问我"。开发期最省事的做法:

# Flask 侧安装并启用 CORS 支持 # pip install flask-cors 后: from flask_cors import CORS CORS(app) # 全部接口放行所有来源 —— 仅限开发期 # 生产环境应收紧为: # CORS(app, origins=["你的正式域名"], supports_credentials=True)

分寸要拿捏:全放行等于对全世界拆门,生产环境必须收窄到自己的域名。前端开发期还有个更朴素的绕法:让页面和 API 走同一个源——把前端页面也交给 Flask 的静态服务,跨域问题从根上不存在,6.2 的项目结构正是这么安排的。

演练:重做轻记账首页

把 4.4 的模板页改造成 Fetch 版:

<ul id="record-list"></ul> <form id="add-form"> <input id="item" placeholder="用途"> <input id="amount" type="number" step="0.01" placeholder="金额"> <button type="submit">记一笔</button> </form> <script> function renderList(records) { const box = document.getElementById("record-list"); box.innerHTML = records.map(r => `<li>${r.item} — ${r.amount} 元(${r.date})</li>`).join(""); } document.getElementById("add-form").addEventListener("submit", function (e) { e.preventDefault(); // 拦下表单默认行为,交给 Fetch addRecord( document.getElementById("item").value, Number(document.getElementById("amount").value), ); }); loadRecords(); // 页面打开即拉数据 </script>

打开页面,列表从 API 实时渲染;提交一笔,列表无刷新追加。注意模板引擎的角色变化:4.4 里它负责把数据织进 HTML,现在页面是静态壳,数据全部由 Fetch 注入——模板只管骨架,不再参与数据。变式一:给列表加删除按钮,点击后调 DELETE 接口再重拉。变式二:故意停掉后端服务再刷新页面,观察 Fetch 的异常分支怎么走——前端错误处理平时看不见,故障时才显身手。

易错点清单

  • 忘了 await:拿到的是未完成的 Promise,读属性全是 undefined
  • 只写成功分支:网络一断页面毫无反应,用户以为按钮坏了
  • 开发期全放行带上线:CORS 全开等于跨站攻击的邀请函
  • 前端存了一份数据自己算:与服务器数据漂移后无从排查,单一数据源原则

本节要点回顾

  • Fetch 让页面只换数据不换页:async 加 await,成功失败分层处理
  • POST 显式声明 JSON 内容类型,业务错误读响应体 error 字段
  • 同源策略是浏览器的保护机制,CORS 是服务器的放行声明
  • 生产环境 CORS 必须收窄,或让前后端同源绕开问题
  • 页面变成静态壳加数据注入,前后端分离就此成形

解耦完成,代码也该收拾收拾了。下一节按工程惯例整理项目结构,让 v1.0 的代码长出正规军的队形。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U