打开浏览器开发者工具,前端切到 Network 面板,调用随便点个按钮,后端你就能看到前端是接口怎么跟后端说话的。那一行行请求记录,前端就是调用答案。
最原始的后端方式是 XMLHttpRequest。写起来啰嗦,接口但它是前端所有上层封装的根。下面这段代码我用了很多年,调用现在偶尔调试还会手写一遍:
const xhr = new XMLHttpRequest(); xhr.open('GET',后端 '/api/user?id=1'); xhr.onreadystatechange = function() { if (xhr.readyState === 4 && xhr.status === 200) { console.log(JSON.parse(xhr.responseText)); } }; xhr.send(); 回调地狱的味道扑面而来。实际项目里没人愿意这么干,接口除非你在维护十年前的前端老系统。
后来 fetch 出来了,调用原生支持 Promise,后端代码干净不少。比如获取用户列表:
fetch('/api/users') .then(res => res.json()) .then(data => render(data)) .catch(err => console.error('请求挂了', err)); 但 fetch 有个坑:HTTP 状态码 404 或 500 时,它不会 reject,只有网络层错误才走 catch。你得手动检查 res.ok。我见过不少新人在这里翻车,以为接口报错被捕获了,其实数据是 undefined。
真实项目里更常见的是 axios。它把 JSON 解析、超时、拦截器都封装好了。比如统一加 token:
axios.interceptors.request.use(config => { config.headers.Authorization = localStorage.getItem('token'); return config; }); 响应拦截器里还能统一处理 401 跳登录、弹错误提示。这种集中管理比每个请求都写一遍 fetch 舒服得多。
调用接口不只是发请求那么简单。你得考虑跨域。开发阶段前端跑在 localhost:3000,后端在 localhost:8080,浏览器直接拦你。要么后端加 CORS 头,要么前端配代理。webpack-dev-server 的 proxy 配置我写过无数次:
devServer: { proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } } 这样前端请求 /api/user 就会被转发到后端,浏览器以为同源,不拦了。上线后通常用 Nginx 做同样的事。
接口调用的时机也有讲究。页面加载时拉数据,一般放在 componentDidMount 或 onMounted 里。但别在渲染函数里直接发请求,会死循环。我见过一个同事在 React 的 render 里调 fetch,页面直接卡死,控制台刷了几百条请求。
还有并发问题。比如一个页面要同时拿用户信息和订单列表,用 Promise.all 比串行快一倍:
const [user, orders] = await Promise.all([ fetch('/api/user').then(r => r.json()), fetch('/api/orders').then(r => r.json()) ]); 但要注意,只要有一个失败,整个 Promise.all 就 reject。如果某个接口允许失败,得用 Promise.allSettled。
错误处理不能糊弄。网络断了、后端挂了、返回格式不对,每种情况给用户的反馈应该不一样。我习惯在 axios 拦截器里区分:超时提示“网络慢,重试一下”,500 提示“服务开小差了”,401 直接踢到登录页。别把所有错误都弹一个“请求失败”,用户会疯。
最后说个细节:请求取消。用户快速切换页面时,上一个页面的请求还在飞,回来 setState 会警告。用 AbortController 可以取消 fetch:
const controller = new AbortController(); fetch('/api/data', { signal: controller.signal }); // 离开页面时 controller.abort(); axios 也有 CancelToken,虽然新版改用 AbortController 了。这个点很多人忽略,但线上环境用户手速快的时候,不取消请求就是给自己埋雷。
前端调后端接口,说到底就是发 HTTP 请求、处理响应、管理状态。工具在变,从 XHR 到 fetch 再到 axios,核心逻辑没变。把跨域、错误、取消这几件事处理干净,基本就稳了。