前言
鸽了好久没写文章了,最近准备开始学习Vue3的源码、设计模式、算法、小程序、app等。这篇文章致力于让小白
迅速上手
,不涉及源码等难理解的内容。主要聊一聊原生Ajax请求
,到Axios工具库
,最后聊一聊ES6提出的Fetch。
如果看完还有疑惑的,欢迎在评论区留言,一起讨论,共同进步!
背景
我记得我在刚进公司实习的时候,对于网络请求没有什么概念,都用着大佬封装好的Axios(我那时候连Axios是什么都不知道,更别说Ajax了),由于GET和POST的请求参数不同,像GET请求不能发送请求体(body),这就导致我经常获取不到后端返回的正确数据。
说了这么多,其实就是想表达网络请求挺重要的
,几乎每个页面都需要用到!
什么是网络请求?
通俗地来说,就是我们前端同学
调用
API,请求
后端同学的URL路径,顺便把后端同学需要的参数
一起携带上,然后就等着后端同学给我们响应。这个过程,我认为就是一次网络请求(至于更深层次的,涉及到协议,计算机网络的就不讨论了,其实是我不懂,后续等我学习下,再把这段内容替换掉哈哈哈)
。一旦得到了成功的响应,我们前端同学再拿到对应的数据进行展示。
什么是Ajax?
Ajax(异步的JS和XML)
不是一门新的语言
,它其实是还是JS。它能在网页不刷新
的时候发送HTTP请求并获取响应。
Ajax的优缺点?
- 优点:
- 【我认为最大的优点】
不刷新页面
。这个可以减少等待时间,大大提高用户的体验感;- 可以根据用户的事件来
更新部分页面
(如:列表搜索,只刷新列表的内容,并没有刷新整个网页)。- 缺点:
- 不能回退(浏览器不能前进、后退);
- 存在跨域问题;
- 对于
SEO(爬虫)不友好
(爬虫只能爬取静态写死的页面,如关键字写死。对于JS动态操作onClick等用户操作或者通过Ajax网络请求到的资源,爬虫很难爬到)。
手写一个原生Ajax请求
// 1.创建一个请求对象
const xhr = new XMLHttpRequest(); // xhr是实例对象:获取该原型对象XMLHttpRequest上的属性和方法
xhr.responseType = 'json'; // 设置响应体的类型为json格式
xhr.timeout = 2000; // 设置超时时间2000ms(2s)
xhr.ontimeout = () => {}; // 网络请求超时的友好提示
xhr.onerror = () => {}; // 网络异常的友好提示
// 2.设置请求方法和请求路径url
// xhr.open('GET', 'http://127.0.0.1:8080/test'); 请求方法也可以为POST,PATCH,DELETE,PUT
// xhr.open('GET', 'http://127.0.0.1:8080/test?a=100&b=200'); 其中?a=100&b=200为查询字符串
xhr.open('POST', 'http://127.0.0.1:8080/test');
// [补充] xhr.open('GET', `http://127.0.0.1:8080/test?t=${Date.now()}`); 解决浏览器缓存问题
// 3.发送请求,直接send()也是可以的,即不传递请求体参数
// xhr.send(); send方法里面可以传递请求体参数,一般用json格式的比较多,需要传递一个字符串类型的参数
const data = JSON.stringfy({
name: 'cxxi',
content: '欢迎大家一起来学习Ajax'
});
xhr.send(data);
// 4.绑定事件,处理响应结果
xhr.onreadystatechange = () => {
// readystate有五种状态:0 1 2 3 4
// change 改变(会触发四次,第一次为0的时候不触发)
if (xhr.readystate === 4) {
// readystate为4的时候,说明得到了服务端的响应
if (xhr.status >= 200 && xhr.status < 400) {
// 设置当状态码在[200, 400)之间的时候,允许处理响应体
xhr.status; // 响应状态码 200
xhr.statusText; // 响应信息 OK
xhr.getAllResponseHeaders(); // 所有响应头信息
xhr.response; // 响应体 body
xhr.abort(); // 可以取消请求,避免请求重复发送
}
}
};
什么是Axios?
通过手写一个Ajax请求之后,相信大家已经加深了印象,不就是调API嘛!其实Axios是
基于Ajax
,在它的基础上进一步封装
,并且与Promise良性结合
在一起的一个工具库。这个库封装了很多东西,开箱即用,当然也可以对这个库自己再封装。
为什么要使用Axios?
至于为什么要使用它,更多原因是别人封装好的,总比自己不断去写原生强吧~它更能实现快速开发。当然在用这个库之前,需要安装下,即
yarn add axios 或者 npm i axios
。(你可以不用写 -D 或者 --dev,因为打包上线后它也会转化成原生写法)
Axios的优势?
- 请求响应
拦截器
- 请求响应数据转换
(Promise)
- 保护组织跨站攻击等
手写一个Axios请求
// 1.导入axios(会去node_modules中寻找axios文件夹下的package.json然后进入index.js)
import axios from 'axios';
// 2.发送一个最简单的axios请求(可选参数详见官网)
axios({
method: 'GET', // 还可以是POST,PUT,PATCH,DELETE
url: 'http://127.0.0.1:8080/test',
params: {} // 查询字符串,可选参数
});
// 这个用法和上面是基本一致的
axios.request({
method: 'POST',
url: 'http://127.0.0.1:8080/test',
data: {} // 请求体,可选参数等
});
// 这三种写法选择一种就行
axios.get('http://127.0.0.1:8080/test', { params }, config); // params为查询字符串参数,config为其他参数
-----------------------到上面为止,一个最简单的请求以及三种书写方式就介绍完了----------------------
// 3.其他一些小优化(axios的默认配置,详见官网)
axios.defaults.baseURL = 'http://127.0.0.1:8080'; // 最终URL = baseURL + 请求路径url
axios.defaults.timeout = 2000; // 设置请求超时时间,单位毫秒(ms)
axios.get('/test'); // 这个最终url = http://127.0.0.1:8080/test
// 当有像不同服务器发送请求的需求时,设置默认值就达不到效果了,因为默认值只能向一个服务器发送请求
// 4.为了解决上述问题,可以使用创建实例的方法(创建两个实例然后导出)
const test1 = axios.create({
baseURL: 'http://127.0.0.1:8080'
});
const test2 = axios.create({
baseURL: 'http://127.0.0.1:5173'
});
export { test1, test2 }; // 当我们要向8080端口发送请求的时候导入{ test1 }即可,5173端口导入{ test2 }
// 5.axios对比ajax的新特性:请求、响应拦截器
axios.interceptors.request.use(config => { // 如果设置多个,则后进先执行
// 请求成功,可以请改config内的参数
return config;
}, error => {
// 失败
return Promise.reject(error);
});
axios.interceptors.response.use(response => { // 如果设置多个,先进先执行
// 响应成功
return response;
}, error => {
// 失败
return Promise.reject(error);
});
-----------------------下面书写一个axios的取消请求,避免重复请求-----------------------
import { test1 as axios } from '@上面创建的test1实例';
const handleCancel = async () => {
let cancel = null; // 2.用来判断请求是否取消
if (!cancel) cancel(); // 4.取消请求。当本次请求成功或者失败的时候,下次请求不会进入if语句
try {
const dataSource = await axios.get('/test', {
// 为了更清楚看到效果,避免请求成功或者失败重置cancel导致进不到if语句,可以设置延迟时间
timeout:5000,
// 1.配置cancelToken对象
cancelToken: new axios.CancelToken(c => {
// 3.赋值
cancel = c;
});
});
if (dataSource.data) cancel = null; // 如果成功响应,并且data不为null(有数据),则重置
} catch (e) {
cancel = null; // 如果请求失败了,则重置
console.log(e);
}
};
Axios源码目录结构
- axios文件夹下
- dist文件夹为打包后的文件
- lib核心文件夹 - 所有源码都放在这个文件夹中
- adapters 适配器
- http.js 服务端请求
- xhr.js 前端ajax请求
- cancel取消相关的文件夹
- Cancel.js 取消时的错误对象
- CancelToken.js 取消请求的构造函数
- isCancel.js 检查参数是否为取消对消
- core核心功能的文件夹
- Axios.js 存放Axios构造函数
- buildFullPath.js 构造完整url
- createError.js 创建error对象
- dispatchRequest.js 创建发送请求(调用两个适配器之一)
- enhanceError.js 更新错误对象的函数文件
- InterceptorManager.js 拦截器对象
- mergeConfig.js 合并对象
- settle.js 根据状态码改变状态
- transformData.js 对结果的转换(格式化)
- helpers 帮助(功能函数)
- bind.js 改变函数运行时的指向
- buildURL.js 设置URL
- combineURLs.js URL合并
- cookies.js 对cookie的操作
- deprecatedMethod.js 没有用过的
- isAbosulteURL.js 判断绝对路径
- isURLSameOrigin.js 判断是否同源(同一个服务)
- normalizeHeaderName.js 对头信息统一,大写
- parseHeaders.js 头信息解析
- spread.js 对ajax请求进行批量结果的处理
- axios.js
真正的入口文件
- index.js 入口文件
- defaults.js 默认配置对象 (axios.default.timeout = )
- utils.js 很多的工具函数
- axios.js(真正的入口文件分析)
- 将方法复制到目标函数中的显示原型链上
- 函数自调用
- 或者实例化对象然后调用实例对象隐式原型链上的方法
- 然后导出改方法
手写一个Fetch请求
// Fetch是一种新的获取资源的接口方式,并不是对XMLHttpRequest的封装。
// 而Axios是对XMLHttpRequest的封装。
Fetch('http://127.0.0.1:8000/test', {
// 第一个参数是url,第二个参数是可配置的参数(具体配置项需要查阅)
method: 'POST', // 请求方法
headers: {}, // 请求头信息配置
body: JSON.stringfy({}), // 请求体为Nodejs中的Buffer类型或者字符串类型或者表单类型
mode: 'no-cors' // 是否允许跨域,同源请求。same-origin(同源请求)、no-cors(默认)和cors(允许跨域请求)
});
总结
现在手写原生Ajax请求基本上我是不怎么写了,除非遇到一些扫码页面,需要缩短响应时间,尽量不在html页面中引入Axios的cdn形式。Axios是我现在用得比较多的,
在项目中使用起来非常方便
。对于Fetch而言,因为Fetch是浏览器原生支持
,不需要额外使用第三方库,减小体积。如果是在浏览器控制台测试,或者想快速请求的话,可以使用Fetch,因为它不需要导入,是浏览器支持的。
预言
哈哈哈哈哈,这次就不预言啦,等把上次预言的内容(债)清一清,大概涉及
Nodejs
和Vite
。