Fetch API
选择或者不选择它?
Fetch是AJAX的替换方案,基于Promise设计,很好的进行了关注分离,有很大一批人喜欢使用fetch进行项目开发;
但是Fetch的缺点也很明显,首先需要明确的是Fetch是一个 low-level(底层)的API,没有帮助你封装好各种各样的功能和实现;
比如发送网络请求需要自己来配置Header的Content-Type,不会默认携带cookie等;
比如错误处理相对麻烦(只有网络错误才会reject,HTTP状态码404或者500不会被标记为reject);
比如不支持取消一个请求,不能查看一个请求的进度等等;
MDN Fetch学习地址:https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch
const data = { username: "example" };
fetch("https://example.com/profile", {
method: "POST", // or 'PUT'
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
})
.then((response) => response.json()) // 把响应体里的 JSON 字符串解析成 JS 对象
.then((data) => {
console.log("Success:", data);
})
.catch((error) => {
console.error("Error:", error);
});
axios
axios是目前前端使用非常广泛的网络请求库
主要特点包括:
在浏览器中发送 XMLHttpRequests 请求;
在 node.js 中发送 http请求;
支持 Promise API;
拦截请求和响应;
转换请求和响应数据;
function getList(){
axios.request({
url:'/article/home/index',
method:'get',
baseURL:'http://test.mediastack.cn/'
}).then(
res => {
console.log("get res:",res);
},error => {
console.log("get request failed:",error);
}
);
}
axios响应拦截器和请求拦截器
拦截器的代码建议单独封装成一个请求工具模块
✅ 这样封装的好处:
-
只在一处配置,全局生效,避免重复逻辑。
-
清晰职责:
api/request.js
专门管理网络请求。 -
项目更模块化、维护更简单。
// src/api/request.js
import axios from 'axios';
// 创建实例
const service = axios.create({
baseURL: 'http://localhost:8888', // 或写环境变量
timeout: 10000,
});
// 请求拦截器
service.interceptors.request.use(
(config) => {
// 请求前的处理逻辑
// 设置请求头格式
config.headers["Content-Type"] = "application/json";
// 添加 Token(身份验证)
try {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
} catch (e) {
console.warn('获取 token 失败', e);
}
return config;
},
(error) => Promise.reject(error) // 请求出错,比如网络断开、拦截器代码异常
);
// 响应拦截器
service.interceptors.response.use(
(response) => {
// 业务逻辑错误处理(可以根据后端返回的 code 来判断)
const res = response.data;
if (res.code !== 0) {
console.error('接口报错:', res.message || '未知错误');
return Promise.reject(new Error(res.message || 'Error'));
}
// 成功响应直接返回数据
return res;
},
(error) => {
// 响应错误处理
if (error.response) {
// HTTP 状态码错误处理(例如:401 未授权)
switch (error.response.status) {
case 401:
alert('登录已过期,请重新登录');
window.location.href = '/login'; // 重定向到登录页
break;
case 500:
alert('服务器出错,请稍后再试');
break;
default:
alert('请求出错,请稍后重试');
break;
}
} else {
console.error('请求错误:', error);
}
// 将错误信息返回给调用者
return Promise.reject(error);
}
);
export default service;
✅ 使用方式(在具体接口文件中)
// src/api/bill.js
import request from './request';
export const getBillListApi = () => request.get('/list');
export const addBillApi = (data) => request.post('/list', data);
🔁 如果使用 redux + createAsyncThunk
// redux 中的异步函数
export const fetchBillList = createAsyncThunk(
'bill/fetch',
async () => {
const res = await request.get('/list');
return res.data;
}
);
示例:
推荐目录结构与命名方式:
/src
├── /services // ✅ 所有接口请求相关的 API 模块
│ ├── index.js // 汇总导出所有模块 API
│ ├── user.js // 用户相关接口
│ ├── auth.js // 权限/登录相关接口
│ ├── menu.js // 菜单相关接口
│ └── ...更多模块
│
├── /utils
│ └── http.js // ✅ axios 实例 + 请求拦截/响应拦截统一封装
✅ 示例一:utils/http.js
(封装 axios 实例)
// utils/http.js
import axios from "axios";
import { getSession } from "next-auth/react";
const http = axios.create({
baseURL:
process.env.NODE_ENV === "development"
? "http://localhost:3000/proxy"
: "https://api.jsonlee.cn",
timeout: 10000,
headers: {
"Content-Type": "application/json",
},
});
http.interceptors.request.use(
async (config) => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => Promise.reject(error)
);
http.interceptors.response.use(
(response) => {
const res = response.data;
if (res.code !== 0) {
return Promise.reject(res.message || "接口请求失败");
}
return res;
},
(error) => Promise.reject(error)
);
export default http;
✅ 示例二:services/menu.js
(菜单模块接口)
// services/menu.js
import http from "@/utils/http";
export const getMenuAuthority = (authorityId) =>
http.post("/menu/getMenuAuthority", { authorityId });
export const getAllMenus = () => http.get("/menu/getAllMenus");
✅ 示例三:services/index.js
(统一导出接口)
// services/index.js
export { getMenuAuthority, getAllMenus } from "./menu"; // 只导出部分 menu 模块 API
export * from "./auth"; // 导出 auth 模块所有导出
export * from "./user"; // 导出 user 模块所有导出
组件里调用:
import { getMenuAuthority } from "@/services";