网络请求 fetch 完整教程(请求后端数据)
前两篇我们学了异步编程。为什么要学它?因为真正的项目里,数据几乎都在”服务器”上,不在你的页面里。页面要向服务器”要数据”,这个动作就是网络请求。
这一篇教你怎么用 fetch 发出请求、拿到数据、处理错误,并且会拿一个真实的公开 API 来实战。
一、先理解:网页和服务器怎么对话?
你的浏览器(前端)向服务器(后端)发出请求,服务器返回数据,浏览器再把数据显示出来。就像你去餐厅:你点菜(请求),厨师做好端上来(响应)。
这个”点菜-上菜”的对话,用的协议叫 HTTP。其中最常见的两种”点菜方式”:
| 方法 | 作用 | 比喻 |
|---|---|---|
| GET | 获取数据 | 看菜单,不改变任何东西 |
| POST | 提交数据 | 下单,把新东西交给服务器 |
比如:
- 打开一个新闻网站 → 浏览器发 GET 请求,服务器返回新闻列表
- 你登录账号 → 浏览器发 POST 请求,把用户名密码交给服务器
二、fetch 的基本用法(GET 请求)
fetch 是浏览器内置的函数,专门用来发网络请求。最简单的用法:
fetch("https://example.com/api/data") .then(response => response.json()) .then(data => { console.log(data); });一步步拆解
// 1. 发出请求,fetch 返回一个 Promiselet promise = fetch("https://example.com/api/data");
// 2. 等响应:response 是一个"响应对象",要先调用 .json() 把数据解析出来promise .then(response => response.json()) // .json() 也返回 Promise .then(data => { // 这里的 data 才是真正的数据 console.log(data); });为什么有两次 then? 第一次拿到的是”响应信封”(response),信封里装着数据,要用 .json() 拆开;.json() 本身也是异步的(要读完整数据),所以又要等一次。
用 async/await 写(推荐,清晰得多)
async function getData() { let response = await fetch("https://example.com/api/data"); let data = await response.json(); console.log(data);}
getData();和上面的 then 版本完全等价,但读起来像同步代码,推荐以后都用这种写法。
三、实战:请求一个真实的公开 API
我们用一个完全免费、无需密钥的公开 API:JSONPlaceholder,它专门给新手练手,返回假数据。
async function 获取文章() { let response = await fetch("https://jsonplaceholder.typicode.com/posts"); let posts = await response.json(); console.log(posts);}
获取文章();打开浏览器控制台运行,几秒后你会看到一长串数据——每篇”文章”长这样:
{ "userId": 1, "id": 1, "title": "sunt aut facere repellat provident occaecati", "body": "quia et suscipit..."}你已经成功从服务器拿到数据了! 这就是全栈开发中”前端请求数据”的核心一步。
把数据显示到页面上
拿到的数据要展示给用户,得操作 DOM:
<!DOCTYPE html><html><head> <meta charset="UTF-8"> <title>文章列表</title></head><body> <h1>文章列表</h1> <ul id="postList"></ul>
<script> async function 加载文章() { let response = await fetch("https://jsonplaceholder.typicode.com/posts"); let posts = await response.json();
let list = document.getElementById("postList"); for (let post of posts.slice(0, 5)) { // 只显示前 5 篇 let li = document.createElement("li"); li.textContent = post.title; list.appendChild(li); } }
加载文章(); </script></body></html>用浏览器打开,等一两秒,页面上就会出现 5 条文章标题。
四、POST 请求:向服务器提交数据
GET 是”要数据”,POST 是”交数据”。提交登录、发评论、注册账号都用 POST。
async function 提交数据() { let response = await fetch("https://jsonplaceholder.typicode.com/posts", { method: "POST", // 指定方法 headers: { "Content-Type": "application/json" // 告诉服务器:我发的是 JSON }, body: JSON.stringify({ // 把 JS 对象转成 JSON 字符串 title: "我的新文章", body: "这是我的内容", userId: 1 }) });
let data = await response.json(); console.log(data);}
提交数据();两个关键点:
JSON.stringify(对象):把 JS 对象转成字符串。因为网络传输的只能是一串文字,所以要”序列化”headers:告诉服务器发送内容的格式。JSON 就写"Content-Type": "application/json"
服务器返回的数据里,会包含新创建数据的 id,表示创建成功。
五、错误处理:网络请求一定会出错
请求网络可能遇到各种问题:断网、服务器挂了、接口地址写错、返回的不是 JSON……
两种错误要区分
第一种:网络层错误(根本没连上)——fetch 会直接失败,进 catch:
async function 请求() { try { let response = await fetch("https://不存在的地址.com"); let data = await response.json(); console.log(data); } catch (error) { console.log("请求失败:", error); }}第二种:HTTP 错误(连上了,但服务器返回了错误状态)——比如 404(找不到)、500(服务器出错)。注意:fetch 对这类错误不会进 catch,response 还是能拿到,只是 response.ok 是 false:
async function 请求() { let response = await fetch("https://jsonplaceholder.typicode.com/posts/999999"); console.log(response.ok); // false(这个 id 不存在,返回 404)
if (!response.ok) { console.log("出错了,状态码:", response.status); // 404 return; } let data = await response.json(); console.log(data);}正确的完整写法: 两者都要处理
async function 安全请求(url) { try { let response = await fetch(url);
if (!response.ok) { throw new Error(`请求失败:${response.status}`); // 主动抛错 }
let data = await response.json(); return data; } catch (error) { console.log("出错了:", error.message); return null; }}
安全请求("https://jsonplaceholder.typicode.com/posts/1");throw new Error(...) 会主动制造一个错误,让后面的 catch 接住——这是”把 HTTP 错误统一成异常处理”的常用手法。
六、完整实战:带加载状态的天气查询页
把前面所有知识串起来:输入城市名 → 请求天气 API → 显示结果,并处理”加载中”和”出错”。
我们用 Open-Meteo 的免费天气 API(无需密钥)。它需要经纬度,所以先用另一个免费接口 geocoding-api 把城市名转成经纬度。
<!DOCTYPE html><html><head> <meta charset="UTF-8"> <title>天气查询</title> <style> body { font-family: sans-serif; max-width: 400px; margin: 40px auto; } #result { margin-top: 20px; padding: 20px; background: #f0f4ff; border-radius: 10px; } .loading { color: #888; } .error { color: red; } </style></head><body> <h1>🌤 天气查询</h1> <input id="city" placeholder="输入城市名,如 beijing"> <button id="search">查询</button> <div id="result">输入城市后点查询</div>
<script> let btn = document.getElementById("search"); let input = document.getElementById("city"); let result = document.getElementById("result");
btn.addEventListener("click", async () => { let city = input.value.trim(); if (!city) return;
result.textContent = "加载中..."; result.className = "loading";
try { // 第一步:城市名 → 经纬度 let geoRes = await fetch(`https://geocoding-api.open-meteo.com/v1/search?name=${city}&count=1`); let geoData = await geoRes.json(); let place = geoData.results && geoData.results[0];
if (!place) { throw new Error("找不到这个城市"); }
// 第二步:用经纬度查天气 let lat = place.latitude; let lon = place.longitude; let weatherRes = await fetch(`https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${lon}¤t_weather=true`); let weather = await weatherRes.json();
// 第三步:显示结果 let temp = weather.current_weather.temperature; let wind = weather.current_weather.windspeed; result.className = ""; result.innerHTML = ` <h2>${place.name}</h2> <p>温度:${temp} °C</p> <p>风速:${wind} km/h</p> `; } catch (error) { result.className = "error"; result.textContent = "查询失败:" + error.message; } }); </script></body></html>这段代码演示了真实项目的完整流程:
- 用户点击 → 先显示”加载中…”
- 两次 fetch:先查经纬度,再查天气(第二次请求依赖第一次的结果,用 await 按顺序等)
- 成功 → 把数据显示到页面(用模板字符串拼 HTML)
- 失败 → 显示错误信息
保存成 HTML 用浏览器打开,输入 beijing 点查询,等一两秒就能看到北京的温度和风速。
七、几个实战小技巧
1. 请求参数拼接
GET 请求的参数拼在 URL 后面,用 ? 和 &:
// https://api.example.com/search?keyword=手机&page=2&size=10let url = `https://api.example.com/search?keyword=${keyword}&page=2&size=10`;2. 设置超时(防止一直转圈)
用 AbortController 让请求最多等 10 秒:
let controller = new AbortController();let timer = setTimeout(() => controller.abort(), 10000);
try { let response = await fetch(url, { signal: controller.signal }); // ...} catch (error) { console.log("请求超时或失败");} finally { clearTimeout(timer);}3. 请求带 token(登录后的常见需求)
在 headers 里加 Authorization 字段:
let response = await fetch(url, { headers: { "Authorization": "Bearer 你的token" }});八、总结
这一篇的关键收获:
- HTTP 基础:GET 获取数据,POST 提交数据
- fetch 基本用法:
fetch(url)→response.json()→ 数据 - async/await 版本:清晰、推荐、主流
- POST 请求:method、headers、JSON.stringify 序列化
- 两类错误:网络错误(catch)、HTTP 错误(response.ok 检查)
- 实战:文章列表、天气查询,都是真实项目的标准写法
学会了 fetch,你的网页就真正”活”起来了——能向服务器要数据、交数据。下一篇我们回头补HTML 和 CSS,把页面做得更漂亮。