封装网络请求
前言
最近开新项目又得封装网络请求, 之前的用响应式变量有点折磨, 这次直接用 axios 了, 正好水一篇博客吧.
基本用法
javascript
import axios from 'axios';
const instance = axios.create({
baseURL: 'http://localhost:3000',
timeout: 5000,
});
export { instance };
对 response 和 request 的处理
之后 axios.get 或者 post 什么的就行了. 但是用的时候会发现, 每次都需要这样子
javascript
// 这里假设100为请求成功, 1为token无效或缺少
//
//interface Response{
// message: string,
// code: number,
// data: any
//}
//
//
instance.get('/info').then((res) => {
if (!res.data) {
// 数据为空
return;
}
const { code } = res.data;
if (code === 1) {
router.push('/login');
userStore.clear();
showMsg('请重新登录');
}
const { data } = resp.data;
return data;
});
这样的话每次都需要判断 code, 然后从 res.data 里拿 data. 在页面里面其实并不关心 code, 如果能找个什么地方统一处理的话可以省好多事情.
axios 提供了两个拦截器, axios.interceptors.request.use
和axios.interceptors.response.use
, 一个针对请求, 一个针对回复. 在这里可以用回复来统一处理
javascript
const codeHandler = {
// 处理业务错误
1: () => {
router.push('/login');
userStore.clear();
showMsg('请重新登录');
},
};
const httpCodeHandler = {
// 处理http状态码错误
// ...
};
instance.interceptors.response.use(
(resp) => {
const { code } = resp.data;
if (code !== 100) {
codeHandler[code]?.(resp);
return promise.reject(resp);
}
const { data } = resp.data;
return data;
},
(error) => {
const { status } = error;
httpCodeHandler[status]?.();
return Promise.reject(error);
}
);
这样子的话每次请求就可以直接使用 data 而不需要在意 code 了
javascript
instance
.get('/info')
.then((data) => {
// 做点成功做的事情
})
.catch((err) => {
console.error(err);
});
这里也可以用一下 request 的拦截器, 之后不用再手动加 token
javascript
instance.interceptors.request.use((config) => {
if (!config.url.startsWith('/auth')) {
// 不需要鉴权
return config;
}
// 获取token操作
const token = localstorage.getItem('token');
if (!token) {
router.push('/login');
return;
}
config.headers.Authorization = `Bearer ${token}`;
return config;
});
解耦合
写到这里之后用的还算方便, 如果业务逻辑一直这么简单的话. 但是随着项目的庞大, 这个可能就存在一点问题. 现在的话其实路由/页面、仓库、网络请求耦合在一起了, 在更加复杂的情况下要做修改就会相当痛苦. 这里可以用发布订阅的设计模式来解耦合.
javascript
class EventBus {
on(eventName, listener) {
if (!this.listeners[eventName]) {
this.listeners[eventName] = new Set();
}
this.listeners[eventName].add(listener);
}
emit(eventName, ...args) {
this.listeners[eventName].forEach((listener) => listener(...args));
}
off(eventName, listener) {
if (!this.listeners[eventName]) {
throw new Error(`Event with name ${eventName} is not found`);
}
this.listeners[eventName].delete(listener);
}
}
export default new EventBus();
这样的话, 网络请求这里就不必再导入 useUserStore 和 useRouter 之类的函数了, 只需要导入 EventBus, 然后这样
网络请求
javascript
const codeHandler = {
// 处理业务错误
1: () => {
eventBus.emit('API:UN_AUTH');
},
};
仓库
javascript
export const useUserStore = defineStore('user', () => {
eventBus.on('API:UN_AUTH', () => {
logout();
});
const userInfo = ref();
const setUser = (user) => {
userInfo.value = user;
};
const logout = () => {
userInfo.value = null;
localstorage.removeItem('token');
};
return {
userInfo,
setUser,
logout,
};
});
路由
javascript
const router =
createRouter();
// ...
eventEmitter.on('API:UN_AUTH', () => {
router.push('/auth/login');
});
页面
javascript
eventEmitter.on('API:UN_AUTH', () => {
showMsg('...');
});
这样这几个部分只需要在自己的地方做好自己的事情就可以了.