赞
踩
我们接下来就以axios为例,进行网络请求的二次封装。
首先我们在项目中安装下axios:
npm install axios
安装好依赖后,在使用的地方直接引入就可以了:
<script setup>
import axios from 'axios';
...
axios({
url: '',
})
</script>
打开页面运行一下,可以看到axios已经执行了,只是没有正确的请求地址,所以报错404了。
我们用node来写个简单的接口服务,模拟下接口请求的过程,创建server.js文件,node不是我们的课程内容,这里就直接粘贴代码了,代码如下:
const express = require('express');
const http = require('http');
const app = express();
const server = http.createServer(app);
app.get("/api/test",(req, res)=>{
res.send({state:true, code:200, message:"接口请求成功", data:{}})
})
server.listen(9527);
console.log('服务已经启动,端口9527');
在server.js文件夹下,运行node server.js
启动服务。
有了服务后,我们就可以修改下axios的请求地址为http://127.0.0.1:9527/api/test
,在浏览器中直接打开这个地址是可以获取到返回结果的,说明我们的服务没有问题,但在js中我们都知道这样请求会报跨域问题。
我们可以通过代理的方式解决跨域的问题,因为使用的前端构建工具是vite,添加proxy代理需要在vite.config.js中,在vite.config.js中添加如下代码:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
server:{
proxy:{
// 添加代理
'/api': 'http://127.0.0.1:9527'
}
}
})
将axios请求的url删除域名端口号部分,运行后可以看到,成功接收到了后端返回的数据信息。
这就是axios的简单使用,axios并不需要app.use来使用,结合前面的章节,说明axios并没有install方法,而我们在开发过程中,一般也不会直接去使用axios,而是在axios的基础上进行二次封装使用,那么要怎么封装呢?
很多关于axios封装的代码都会在main.js文件中进行全局属性的配置,比如设置axios的超时时间:
axios.defaults.timeout = 20000;
虽然这样配置并没有什么问题,但网络请求一般在我们的开发中会作为一个单独模块进行封装,对外统一接口,这样的好处是我们可以在一个集中的地方进行网络请求的相关配置,还可以对请求进行一些预处理和返回结果的拦截处理,也不会与main.js文件中本身的逻辑混合。
我们在api文件夹下封装我们的网络请求模块,新增index.js文件:
import axios from "axios";
export default axios;
目前我们只是引入了axios并进行导出,在这个模块中,我们要完成以下功能的封装:
接下来我们就开始依次完成这些功能的封装。
首先思考下,添加请求和响应的拦截目的是什么?一般的网站都存在登录模块,需要用户登录后才可以访问网站内容,那服务是如何判断用户身份和登录状态,相信大家都知道使用token,在用户操作发送请求时携带用户的token信息到后端进行验证。我们肯定不希望在每个请求中都要添加token处理的代码,那就可以在请求拦截器中进行统一处理。
相应的,如果我们对接口返回的结果有统一的处理逻辑,比如对登录失效的错误码进行跳转登录页的操作,也可以放在我们的响应拦截中处理。
总结一下,当我们的网络请求存在相同的处理逻辑时,可以在统一的拦截入口中进行处理,保证对每一个请求都生效。
我们可以通过axios.interceptors.request.use
和axios.interceptors.response.use
来分别对请求和响应实现拦截:
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
console.log('请求拦截', config);
return config;
}, function (error) {
return Promise.reject(error);
});
// 响应拦截
axios.interceptors.response.use(function (response) {
console.log('响应拦截', response);
return response;
}, function (error) {
return Promise.reject(error);
});
我们可以获取到请求的config和响应的response信息,headers中就可以添加token信息,当然我们也可以根据其他参数进行更多的处理,比如取消重复请求。
在网页中,如果一些触发事件没有增加节流防抖,可能会出现用户多次点击,这样的重复并没有什么意义反而会消耗资源,我们在axios的请求中,可以通过内部提供的CancelToken
来取消重复的网络请求。
先来看一下取消重复请求的原理,我们对每个请求生成一个独一无二的key值,在发送请求的时候,将这个key值保存起来,当有重复的请求发送时,我们判断当前key值的请求正在进行中,就调用aixos提供的取消请求的方法取消当前请求,请求返回后,再将key值从保存中移除,让下一次请求可以成功发送。
原理看上去很简单,增加了一个开关来控制请求得发送,我们来看下具体的实现。
首先我们需要一个独一无二的key值来区分当前的请求,对相同的请求过滤,相同的概念不仅仅是指请求的路径方法相同,而是要包括参数也要相同,比如在查询列表的时候,用户可能修改了查询条件再次调用,两次调用会得到不同的结果,这就是两个不同的请求了。
所以我们可以根据请求的地址,方式,参数,统一计算出当前请求的md5值作为key:
import md5 from "md5";
const getRequestKey = (config) => {
if ( !config ){
// 如果没有获取到请求的相关配置信息,根据时间戳生成
return md5(+new Date());
}
const data = typeof config.data === 'string' ? config.data : JSON.stringify(config.data);
return md5(config.url + '&' + config.method + '&' + data);
};
通过getRequestKey方法,在请求拦截中,就可以根据请求的config计算出当前请求的key值了,除了getRequestKey外,我们还需要能够检查key是否存在的方法以及删除key的方法。
// 存储key值
const pending = {};
// 检查key值
const checkPending = (key) => !!pending[key];
// 删除key值
const removePending = (key) => {
delete pending[key];
};
现在我们已经可以计算请求的key值,并且可以判断是否有相同的key值正在请求中以及删除完成了请求的key值。
接下来只需要在请求拦截器中存储判断key值,调用axios的取消请求方法即可:
const CancelToken = axios.CancelToken; // 请求拦截器 axios.interceptors.request.use(function (config) { // 计算当前请求key值 const key = getRequestKey(config); if ( checkPending(key) ) { // 重复请求则取消当前请求 const source = CancelToken.source(); config.cancelToken = source.token; source.cancel('重复请求'); } else { // 加入请求字典 pending[key] = true; } return config; }) // 响应拦截器 axios.interceptors.response.use(function (response) { // 请求完成,删除请求中状态 const key = getRequestKey(response.config); removePending(key); return response; })
我们给server中的接口响应增加3秒的延迟,看下重复请求是否被取消了。
app.get("/api/test",(req, res)=>{
// 使用setTimeout延迟接口的返回
setTimeout(() => {
res.send({state:true, code:200, message:"接口请求成功", data:{}})
}, 3000);
})
我们还可以进一步将取消请求和路由跳转结合,在路由跳转的时候取消所有请求,大家可以自己去尝试下。
完成了axios的一些拦截配置后,我们还需要对外统一get,post等方法:
/** * get方法,对应get请求 * @param {String} url [请求的url地址] * @param {Object} params [请求时携带的参数] */ export function get(url, params = {}, config = {}) { return axios.get(url, { params: params, ...config }) } /** * post方法,对应post请求 * @param {String} url [请求的url地址] * @param {Object} params [请求时携带的参数] */ export function post(url, params, config = {}) { return axios({ url, method: 'post', data: params, ...config }) }
除了上面对重复请求的封装,在实际开发过程中,我们还会遇到跨域,超时,错误码处理等功能,这些都是可以在axios封装的时候进行处理。
// 设置请求头
axios.defaults.headers['xxx'] = 'xxx';
// 设置接口超时时间
axios.defaults.timeout = 30000;
// 配置请求的根路径
axios.defaults.baseURL = 'xxx';
// 跨域时,是否携带用户凭证
axios.defaults.withCredentials = true
本节中,我们介绍了Vue中如何进行网络请求,引用axios进行了二次封装,在二次封装中,我们完成了对请求和响应的拦截,重复请求的取消并对外提供统一的接口调用。
网络请求是前端与后端沟通的重要手段,不仅仅是我们在开发过程中引入一个组件发送一个请求这么简单,在这个过程中浏览器替我们实现了很多内容,从请求的构建到DNS的解析,TCP和HTTP的连接建立到协议的组成部分,最终请求到达服务器进行网络响应,网络请求是一个很大的内容,这中间有太多的细节可以深入去聊,希望大家在课余不妨去梳理下网络请求的整个流程,丰富自己的知识图谱。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。