JavaScript 如何做请求封装
很多做前端开发的朋友,刚开始写代码时,总是会在各个页面里重复写调用接口的代码:写一遍发请求的逻辑,写一遍处理返回数据的逻辑,再写一遍报错的处理逻辑。久而久之,项目里就会出现大量重复的代码,改一个请求配置要找遍所有文件,出了问题也难排查。这时候就需要用请求封装来解决这些麻烦,它就像一个“统一的快递收发站”,把所有发请求的流程都整理好,需要发请求时直接来这里取配置就行。
先搞懂:为什么要做请求封装?
举个生活化的例子:你平时寄快递,每次都要自己找纸箱、缠胶带、填收件人地址,不仅费时间还容易出错。如果有个快递站,你只需要告诉工作人员“我要寄到XX地址”,剩下的打包、贴单、找快递员全由他们搞定,是不是省心很多?

请求封装也是这个道理。比如你要调用一个获取用户信息的接口,封装前你可能会写:
`javascript
fetch('https://你的接口地址/user/info', {
method: 'GET',
headers: { 'token': '当前用户的登录令牌' }
})
.then(res => res.json())
.catch(err => console.log('请求出错了', err));
`
要是换一个接口,还要重复写headers、catch这些内容,万一令牌过期了,每个地方都要改,太麻烦。封装后,你只需要写request('/user/info', 'GET')就行,剩下的统一处理。
第一步:搭一个简单的请求封装骨架
我们可以用最常用的fetch或者axios来做封装,这里用fetch举例子(axios的逻辑类似,只是语法稍有不同)。先建一个request函数,把固定的配置都写进去。
比如,所有请求都需要带登录令牌,都要处理返回的格式,出错了要统一提示,这些都可以放在封装里:
`javascript
// 基础配置:接口前缀,这样写不同环境(开发/生产)不用改接口地址
const BASE_URL = 'https://你的项目接口地址';
async function request(url, method = 'GET', data = null) {
// 从本地存储拿登录令牌,所有请求自动带上,不用每次写
const token = localStorage.getItem('userToken');
// 统一的请求头,固定的内容直接放这里
const headers = {
'Content-Type': 'application/json',
'token': token || ''
};
// 拼接完整的接口地址
const fullUrl = BASE_URL + url;
// 处理请求参数,GET请求放地址里,POST/PUT放请求体里
const options = { method, headers };
if (data && ['POST', 'PUT'].includes(method)) {
options.body = JSON.stringify(data);
}
try {
// 发请求,返回结果
const response = await fetch(fullUrl, options);
const result = await response.json();
// 统一处理业务逻辑:比如后端返回的code代表状态,code为401说明令牌过期,统一跳登录页
if (result.code === 401) {
localStorage.removeItem('userToken');
window.location.href = '/login';
return Promise.reject('登录已过期');
}
return result;
} catch (err) {
// 统一处理报错:比如网络不好时弹提示
console.log('请求失败,请检查网络', err);
return Promise.reject(err);
}
}
`
第二步:用封装好的函数发请求,有多省心?
封装完成后,不管是获取用户信息、提交表单还是删除商品,都只需要简单调用,不用再重复写一堆逻辑。比如:
`javascript
// 1. 获取用户信息(GET请求)
request('/user/info').then(res => {
console.log('用户信息', res.data);
});
// 2. 提交用户注册信息(POST请求)
request('/user/register', 'POST', {
username: '小明',
password: '123456'
}).then(res => {
alert('注册成功');
});
// 3. 删除商品(DELETE请求)
request('/product/123', 'DELETE').then(res => {
alert('删除成功');
});
`
要是以后需要改逻辑,比如把fetch换成axios,或者修改令牌的存储位置,只需要改封装里的request函数,所有调用它的地方都会自动更新,完全不用动业务代码。
最后:封装时要注意的小细节
很多新手封装时会忽略几个小问题,导致踩坑:
一是环境区分:开发时用本地测试接口,上线后用正式接口,封装里可以加个判断:
`javascript
const BASE_URL = process.env.NODE_ENV === 'development'
? 'http://localhost:3000'
: 'https://你的正式接口地址';
`
二是参数兼容:GET请求的参数要拼在地址后面,不能直接放请求体里,封装时要单独处理GET的参数拼接;
三是错误统一:不要只在封装里打日志,还要给业务代码留处理错误的入口,比如request返回Promise,业务代码可以自己处理特定的错误场景。
这样一套封装下来,不仅代码少了一半,后续维护、改需求也会轻松很多,是前端开发里非常实用的小技巧。