一、JavaScript XHR、Fetch
1.1 前端数据请求方式
-
后端:服务器端
-
服务器端渲染 SSR(后端渲染):全部都是后端开发的
- 客户端发送请求
- 服务端接收请求并利用 jsp/asp/php 返回相应 HTML 文档
- 页面刷新,客户端加载新的HTML文档
-
SSR 的缺点
- 页面本质上只有一些数据发生了变化,而服务器却要重绘整个页面,违背了 DRY(Don’t repeat yourself)原则
- 给网络带宽带来不必要的开销
-
前后端分离(客户端渲染、前端渲染)
- 后端只需要关心 API 服务器(提供数据的服务器)和数据库
- AJAX:
- 在不重新刷新网页的情况下与服务器通信,交换数据,或更新页面(无页面刷新获取服务器数据的技术)
- 接收并使用从服务器发来的数据
- 异步的 JavaScript 和 XML(不用了,现在使用 Json)
1.2 HTTP
- 超文本传输协议:用于分布式、协作式和超媒体信息系统的应用层协议
- 最早是为了接收 html 页面,浏览器进行渲染
- http 是一个客户端和服务端之间请求和响应的标准
- 通过 HPPT 或者 HTTPS( http上增加的一个安全层)协议请求的资源由统一资源标识符 URL 来标识
- 通过使用网页浏览器、网络爬虫或者其它的工具,客户端发起一个HTTP请求到服务器上指定端口(默认端口为80)
- 这个客户端为用户代理程序(user agent)
- 响应的服务器上存储着一些资源,比如 HTML文件和图像
- 这个响应服务器为源服务器(origin server)
- 通过使用网页浏览器、网络爬虫或者其它的工具,客户端发起一个HTTP请求到服务器上指定端口(默认端口为80)
- http 就是和服务器沟通的一种协议:请求对象、响应对象
- 网页中的资源通常是被放在 web 资源服务器中,由浏览器自动发送 http 请求来获取、解析、展示的
1.3 网页中资源的获取
- 网页中的资源通常是被放在 Web 资源服务器中,由浏览器自动发送 HTTP 请求来获取、解析、展示的
- **页面中很多数据是动态展示的:**页面中的数据展示、搜索数据、表单验证等等,也是通过在 JavaScript 中发送 HTTP 请求获取的
1.4 HTTP 的组成
-
一次 http 请求主要包括:请求和响应
-
请求
-
请求行:方法(请求方式)、URL、协议版本(目前采用的是什么版本)
-
请求头:客户端会默认传递过来一些信息
-
content-type:请求携带的数据类型(告诉服务器现在传送的数据以什么形式传过去)
- application/x-www-form-urlencoded:以&分隔的键值对(以=分隔)
- application/json:{“”:“”}json类型
- text/plain:普通文本 ×
- application/xml:少用,xml类型
<xml> <name>lili</name> </xml>
- multipart/form-data:上传文件,很多时候不需要手动设置
-
content-length:文件的大小长度
-
keep-alive:不立即断开,继续保持连接一会儿
- node 开发服务器:保持五秒
- http1.1 默认开启了
-
acept-encoding(接收的客户端):告知服务器,客户端支持接收的文件的压缩格式
- zip/rar/7z
- gzip(给你返回更小的压缩文件)
-
accept:我(客户端)能够接收的文件格式(一般为 json)
-
user-agent:客户端相关的信息
-
-
-
响应
- 响应行:协议版本、状态码、状态码的原因短语
- 200 OK 请求成功
- 201 Created POST请求,创建新的资源
- 301 请求资源的URL已经修改,重定向
- 大于四百:错误
- 400 客户端的错误
- 401 未授权的错误
- 403 客户端没有权限访问
- 404 服务器找不到请求的资源
- 500 服务器错误,服务器不知道如何处理
- 503 服务器不可用
- 响应头
- 拿到响应数据:xhr.response
- 可以设置接收的类型:xhr.responseType="json"默认是text
- 响应体
- 响应行:协议版本、状态码、状态码的原因短语
1.5 HTTP 的版本
- HTTP/0.9 版本:1991年,只支持 get 请求方法获取文本数据
- HTTP/1.0:TCP连接
- HTTP/1.1(使用最广泛)
- 发布于1997年
- 增加了PUT、DELETE等请求方法
- 采用持久连接(Connection: keep-alive),多个请求可以共用同一个 TCP 连接
- HTTP/2.0
- HTTP/3.0
1.6 HTTP 的请求方式
- 发送一次请求如何同服务器进行沟通
- RFC(版本规范)中定义了一组请求方式,标识要对给定资源执行的操作
- GET:请求数据,获取数据
- HEAD:返回的数据没有响应体
- POST:提交数据
- PUT:替代目标资源
- DELETE:删除数据
- PATCH:修改数据
- CONNECT:代理服务器
- TRACE:消息环回测试
1.7 AJAX 发送请求
-
第一种方式
-
创建 XMLHttpRequest 对象
-
监听状态的改变(宏任务)
- 一次网络请求中状态发生了很多次变化,数字表示状态
- 0(不监听):代理被创建
- 1 (open):open 方法已经被调用
- 2(send):send 方法已经被调用,并且头部和状态已经可获得
- 3(下载中)
- 4:DONE 已完成
- 这个状态并非是 HTTP 的相应状态,而是记录的 XMLHttpRequest 对象的状态变化:http 响应状态通过 status 获取
- **发送同步请求:**将 open 的第三个参数设置为 false
- 发送请求(浏览器帮助发送对应的请求):实际开发使用异步请求,不会阻塞js代码继续执行:
xhr.open("get", "http://123.207.32.32:8000/home/multidata", false)
- 发送请求(浏览器帮助发送对应的请求):实际开发使用异步请求,不会阻塞js代码继续执行:
- 一次网络请求中状态发生了很多次变化,数字表示状态
-
除了 onreadystatechange 还有其他的事件可以监听
- loadstart:请求开始
- progress:文件上传
- abort:取消了请求
- load:请求成功完成
- loadend
- error:发生连接错误
- onload:只调用一次
- timeout:请求超时
-
配置请求
-
open方法来配置,参数如下:
-
method:请求的方式
-
URL:请求的地址
- json 类型的接口(90%)
- text 类型的接口
- xml 类型的接口
-
// 1. const xhr = new XMLHttpRequest() // 2.onload监听数据加载完成 xhr.onload = function() { // const resJSON = JSON.parse(xhr.response) console.log(xhr.response) // console.log(xhr.responseText) // console.log(xhr.responseXML) } // 3.告知xhr获取到的数据的类型 xhr.responseType = "json" // xhr.responseType = "xml" // 4.配置网络请求 // 4.1.json类型的接口 xhr.open("get", "http://xxxxx") // 4.2.json类型的接口 // xhr.open("get", "http://xxxxx/hello_json") // 4.3.text类型的接口 // xhr.open("get", "http://xxxxx/hello_text") // 4.4.xml类型的接口 // xhr.open("get", "xxxxx/hello_xml") // 5.发送网络请求 xhr.send()
-
<script> // xhr四步骤: // 1. 创建XMLHttpRequest对象 // 所有的东西都放在了 xhr 里面 const xhr = new XMLHttpRequest() // 2. 监听状态的改变(宏任务) xhr.onreadystatechange=function(){ if(xhr.readyState!==XMLHttpRequest.DONE)return // 2.1将字符串转成JSON对象 // const resJSON=JSON.parse(xhr.response) // const banners=resJSON.data.banner log(xhr.response) } // 告知xhr获取到的数据的类型,默认设置为text xhr.responseType="json" // 3. 调用open方法进行配置 //open第三个参数boolean表示同步或者异步,默认true异步 xhr.open("get","https://www.baidu.com",false) // 4. 调用send发送请求 xhr.send() </script>
1.8 获取 http 的状态码
- 前面都是记录 xhr 对象本身的状态变化,并非针对 http 的网络请求状态,http 网络请求的状态需要通过 .status 获取
// 1.创建对象 const xhr = new XMLHttpRequest() // 2.监听结果 xhr.onload = function() { console.log(xhr.status, xhr.statusText) // 根据http的状态码判断是否请求成功 if (xhr.status >= 200 && xhr.status < 300) { console.log(xhr.response) } else { console.log(xhr.status, xhr.statusText) } } xhr.onerror = function() { console.log("onerror", xhr.status, xhr.statusText) } // 3.设置响应类型 xhr.responseType = "json" // 4.配置网络请求 // xhr.open("get", "http://xxxxx/abc/cba/aaa") xhr.open("get", "http://xxxxx/home/multidata") // 5.发送网络请求 xhr.send()
1.9 Get、Post 请求传参
- 要不要传递数据取决于接口需不需要数据
- url
- 主机地址
- 服务器端口号
- 路径
- 常见的传递给服务器数据的方式有如下几种:
- 方式一:GET 请求的 query 参数
- 缺点:以明文的形式直接放在对应的 URL,属于 URL 的一部分,没有那么安全;而 post 请求传递参数的方式有很多种
- 放在请求体里面,FormData 是默认格式
- 缺点:以明文的形式直接放在对应的 URL,属于 URL 的一部分,没有那么安全;而 post 请求传递参数的方式有很多种
- 方式二:POST 请求 x-www-form-urlencoded 格式
- 方式三:POST 请求 FormData 格式
- 方式四:POST 请求 JSON 格式
- 方式一:GET 请求的 query 参数
const formEl = document.querySelector(".info") const sendBtn = document.querySelector(".send") sendBtn.onclick = function() { // 创建xhr对象 const xhr = new XMLHttpRequest() // 监听数据响应 xhr.onload = function() { console.log(xhr.response) } // 配置请求 xhr.responseType = "json" // 1.传递参数方式一: get -> query // xhr.open("get", "http://xxxx/get?name=lili&age=18&address=广州市") // 2.传递参数方式二: post -> urlencoded // xhr.open("post", "http://xxxx/posturl") // // 发送请求(请求体body) // xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded") // xhr.send("name=lili&age=18&address=广州市") // 3.传递参数方式三: post -> formdata // xhr.open("post", "http://xxxx/postform") // 得将元素转成 form 对象 // // formElement对象转成FormData对象 // const formData = new FormData(formEl) // xhr.send(formData) // 4.传递参数方式四: post -> json xhr.open("post", "http://xxx/postjson") xhr.setRequestHeader("Content-type", "application/json") xhr.send(JSON.stringify({name: "lili", age: 18, height: 1.88})) }
1.10 ajax 网络请求封装
function liliajax({ url, method = "get", data = {}, headers = {}, // token success, failure } = {}) { // 1.创建对象 const xhr = new XMLHttpRequest() // 2.监听数据 xhr.onload = function() { if (xhr.status >= 200 && xhr.status < 300) { success && success(xhr.response) } else { failure && failure({ status: xhr.status, message: xhr.statusText }) } } // 3.设置类型 xhr.responseType = "json" // 4.open方法 // 一般不推荐写在 data 里面 if (method.toUpperCase() === "GET") { // 对 data 进行遍历 const queryStrings = [] for (const key in data) { queryStrings.push(`${key}=${data[key]}`) } url = url + "?" + queryStrings.join("&") xhr.open(method, url) xhr.send() } else { xhr.open(method, url) xhr.setRequestHeader("Content-type", "application/json") xhr.send(JSON.stringify(data)) } return xhr } // 调用者 liliajax({ url: "http://xxx/get", method: "GET", data: { name: "lili", age: 18 }, success: function(res) { console.log("res:", res) }, failure: function(err) { // alert(err.message) } }) // liliajax({ // url: "http://xxxx/postjson", // method: "post", // data: { // name: "jsondata", // age: 22 // }, // success: function(res) { // console.log("res:", res) // }, // failure: function(err) { // // alert(err.message) // } // })
function liliajax({ url, method = "get", data = {}, timeout = 10000, headers = {}, // token } = {}) { // 1.创建对象 const xhr = new XMLHttpRequest() // 2.创建Promise const promise = new Promise((resolve, reject) => { // 2.监听数据 xhr.onload = function() { if (xhr.status >= 200 && xhr.status < 300) { resolve(xhr.response) } else { reject({ status: xhr.status, message: xhr.statusText }) } } // 3.设置类型 xhr.responseType = "json" xhr.timeout = timeout // 4.open方法 if (method.toUpperCase() === "GET") { const queryStrings = [] for (const key in data) { queryStrings.push(`${key}=${data[key]}`) } url = url + "?" + queryStrings.join("&") xhr.open(method, url) xhr.send() } else { xhr.open(method, url) xhr.setRequestHeader("Content-type", "application/json") xhr.send(JSON.stringify(data)) } }) promise.xhr = xhr return promise }
const promise = liliajax({ url: "http://xxxx/get", data: { username: "lili", password: "123456" } }) promise.then(res => { console.log("res:", res) }).catch(err => { console.log("err:", err) })
1.11 timeout、手动取消请求
- timeout 浏览器达到过期时间还没有获取对应的结果,取消本次请求
- 可以通过 abort 方法强制取消请求
<button>取消请求</button> <script> const xhr = new XMLHttpRequest() xhr.onload = function() { console.log(xhr.response) } xhr.onabort = function() { console.log("请求被取消掉了") } xhr.responseType = "json" // 1.超市时间的设置 xhr.ontimeout = function() { console.log("请求过期: timeout") } // timeout: 浏览器达到过期时间还没有获取到对应的结果时, 取消本次请求 // xhr.timeout = 3000 xhr.open("get", "http://xxxxx/timeout") xhr.send() // 2.手动取消结果 const cancelBtn = document.querySelector("button") cancelBtn.onclick = function() { xhr.abort() } </script>
1.12 认识 Fetch 和 Fetch API 发送网络请求
- fetch 是 xhr 的代替方案,提供了更加现代的处理方案
- 返回值是一个 Promise
- 在请求发送成功时,调用 resolve 回调 then
- 在请求发送失败时,调用 reject 回调 catch
- 所有的操作并不是都在一个对象上面
- fetch 函数的使用
- 参数
- input
- 定义要获取的资源地址,可以是一个 URL 字符串,也可以使用一个 Request 对象(实验性特性)类型
- init:其他初始化参数
- method: 请求使用的方法,如 GET、POST
- headers: 请求的头信息
- body: 请求的 body 信息
- input
- 参数
// 1.fetch发送get请求 // 1.1.未优化的代码 // fetch("xxxx").then(res => { // // 1.获取到response // const response = res // // 2.获取具体的结果 // response.json().then(res => { // console.log("res:", res) // }) // }).catch(err => { // console.log("err:", err) // }) // 1.2. 优化方式一: // fetch("xxxx").then(res => { // // 1.获取到response // const response = res // // 2.获取具体的结果 // return response.json() // }).then(res => { // console.log("res:", res) // }).catch(err => { // console.log("err:", err) // }) // 1.3. 优化方式二: // async function getData() { // const response = await fetch("http://xxxxx") // const res = await response.json() // console.log("res:", res) // } // getData() // 2.post请求并且有参数 async function getData() { // const response = await fetch("http://xxxxx/postjson", { // method: "post", // // headers: { // // "Content-type": "application/json" // // }, // body: JSON.stringify({ // name: "lili", // age: 18 // }) // }) const formData = new FormData() formData.append("name", "lili") formData.append("age", 18) const response = await fetch("http://xxxx/postform", { method: "post", body: formData }) // 获取response状态 console.log(response.ok, response.status, response.statusText) const res = await response.json() console.log("res:", res) } getData()
1.13 XMLHttpRequest 文件上传
-
文件上传:头像上传、照片等
-
通过表单传给服务器
-
可以查看上传的进度
<input class="file" type="file"> <button class="upload">上传文件</button> <script> // xhr/fetch const uploadBtn = document.querySelector(".upload") uploadBtn.onclick = function() { // 1.创建对象 const xhr = new XMLHttpRequest() // 2.监听结果 xhr.onload = function() { console.log(xhr.response) } xhr.onprogress = function(event) { console.log(event) } xhr.responseType = "json" xhr.open("post", "http://xxxxx/upload") // 表单 const fileEl = document.querySelector(".file") const file = fileEl.files[0] const formData = new FormData() formData.append("avatar", file) xhr.send(formData) } </script>
1.14 Fetch 请求文件上传
<input class="file" type="file"> <button class="upload">上传文件</button> <script> // xhr/fetch const uploadBtn = document.querySelector(".upload") uploadBtn.onclick = async function() { // 表单 const fileEl = document.querySelector(".file") const file = fileEl.files[0] const formData = new FormData() formData.append("avatar", file) // 发送fetch请求 const response = await fetch("http://xxxx/upload", { method: "post", body: formData }) const res = await response.json() console.log("res:", res) } </script>
二、网络请求库 - axios
2.1 axios
- 功能特点
- 在浏览器中发送 XMLHttpRequests 请求
- 在 node.js 中发送 http 请求
- 支持 Promise API
- fetch 也支持 promise
- 拦截请求和响应
- 转换请求和响应数据
- axios: ajax i/o system
2.2 常见配置选项
- 请求地址:url: ‘/user’,
- 请求类型:method: ‘get’,
- 请根路径:baseURL: ‘http://www.xxx.com/api’,
- 请求前的数据处理:transformRequest:[function(data){}],
- 请求后的数据处理:transformResponse: [function(data){}],
- 自定义的请求头:headers:{‘x-Requested-With’:‘XMLHttpRequest’},
- 由服务器决定
- URL查询对象:params:{ id: 12 },
- 查询对象序列化函数:paramsSerializer: function(params){ }
- request body:data: { key: ‘aa’},
- 超时设置:timeout: 1000,
import { createApp } from "vue"; import App from "./App.vue"; import axios from "axios"; createApp(App).mount("#app"); // 1. baseURL const baseURL = "http://xxxxx:8000" // 给axios实例配置公共的基础配置 axios.defaults.baseURL = baseURL axios.defaults.baseURL = 10000 axios.defaults.headers = {} // 1.1 get: axios.get("/home/multidata").then(res => { console.log(res.data); }) // 1.2 get
2.3 axios 请求方式
-
支持多种请求方式
-
axios(config)
-
axios.request(config)
-
axios.get(url[, config])
- 能够直接在浏览器中敲回车发送的请求
-
axios.delete(url[, config])
-
axios.head(url[, config])
-
axios.post(url[, data[, config]])
-
axios.put(url[, data[, config]])
-
axios.patch(url[, data[, config]])
-
import { createApp } from "vue"; import App from "./App.vue"; import axios from "axios"; createApp(App).mount("#app"); //1. 发送 request请求:发送一些配置 axios .request({ url: "http://xxxxx", method: "get", }) .then((res) => { console.log(res.data); }); // 2. 发送get请求: // 传递参数: // 2.1 querry参数 axios.get("http:/xxxxx/lyric?id=500665346", {}).then((res) => { console.log(res.data); }); // 2.2 params这个使用比较多 axios .get("http://xxxxx/lyric", { params: { id: 500665346, }, }) .then((res) => { console.log(res.data); }); // 3. 发送 post 请求:post请求的 url 参数是不能拼接到后面的,只能放在请求体里面 // 两种写法 axios .post("http://xxxxx/postjson", { username: "lili", password: "123455", }) .then((res) => { console.log(res.data); }); axios .post("http://xxxxx/postjson", { data: { username: "lili", password: "123455", }, }) .then((res) => { console.log(res.data); });
- 同时发送两个请求
- 使用 axios.all, 可以放入多个请求的数组
- axios.all([]) 返回的结果是一个数组,使用 axios.spread 可将数组 [res1,res2] 展开为 res1, res2
// 2. axios 发送多个请求 // Promise.all axios.all([ axios.get("/xxx/multidata"), axios.get("/xxx/multidata2") ]).then(res => { console.log(res.data); })
2.4 axios 创建实例
- 对于单独的 url 会创建单独的实例
- 看开发过程中有多少个实例
import { createApp } from "vue"; import App from "./App.vue"; import axios from "axios"; createApp(App).mount("#app"); // axios 默认库提供给我们的实例对象 axios.get("http://xxxxx/lyric?id=500665346"); // 创建其他的实例发送网络请求 const instance1 = axios.create({ baseURL: "http://xxxx", timeout: 6000, headers: {}, }); instance1.get("/lyric", { params: { id: 500665346, }, }); const instance2 = axios.create({ baseURL: "http://xxxxx", timeout: 9000, headers: {}, });
2.5 请求和响应拦截器
- 在发出网络请求之前
- 请求成功
- 显示 loading 动画
- 对原来的配置文件进行修改
- header
- 认证登陆:传入 token、cookie
- 对请求参数进行某些转化
- 请求成功
- 在得到响应结果之前
- 响应成功
- 结束 loading 动画
- 对数据进行转化,再返回数据
- 响应成功
- 进行处理
// 对实例配置拦截器 axios.interceptors.request.use(请求成功回调拦截, 请求失败回调拦截); // 一般都不会请求失败 axios.interceptors.request.use( (config) => { if (config.url === "user/info") config.headers["token"] = "lili"; // 请求成功的拦截 return config; }, (err) => { console.log("请求失败的拦截,基本上不会发生"); } ); // 400 404 请求失败 axios.interceptors.response.use(成功回调, 失败回调); axios.interceptors.response.use( (res) => { // 这里对响应的结果进行转化 // 在得到结果的时候就不需要 res.data 了 // 响应成功的拦截 return res.data; }, (err) => { // 响应失败的拦截 return err; } ); axios .get("http://xxxxx/lyric?id=500665346") .then((res) => { console.log(res); }) .catch((err) => { console.log(err); });
2.6 封装
import axios from "axios"; class LiliRequest { constructor(baseURL, timeout = 10000) { // 创建一个实例进行配置 this.instance = axios.create({ baseURL, timeout }) } request(config) { return new Promise((resolve, reject) => { this.instance .request(config) .then((res) => { resolve(res.data); }) .catch((err) => { reject(err); }); }); } get(config) { return this.request({ ...config, method: "get" }); } post(config) { return this.request({ ...config, method: "post" }); } } const liliRequest1 = new LiliRequest("http://www.baidu.com") const liliRequest2 = new LiliRequest("http://www.taobao.com") export default new LiliRequest()
import liliRequest from './service/index'
三、TypeScript - axios 封装
3.1 基本封装
import axios from "axios"; import type { AxiosInstance, AxiosRequestConfig } from "axios"; class LiliRequest { instance: AxiosInstance; // 1. 创建实例 // request 实例 => axios实例 constructor(config: AxiosRequestConfig) { this.instance = axios.create(config); } // 2. 封装网络请求的方法 request(config: AxiosRequestConfig) { return this.instance.request(config) } get() { } } export default LiliRequest;
拦截器
- 全局拦截
import axios from "axios"; import type { AxiosInstance, AxiosRequestConfig } from "axios"; // 拦截器:蒙版 loading/token/修改配置 class LiliRequest { instance: AxiosInstance; // 1. 创建实例 // request 实例 => axios实例 constructor(config: AxiosRequestConfig) { this.instance = axios.create(config); // 每个instance实例都添加拦截器 // 传入一个config对config进行修改再返回 this.instance.interceptors.request.use( (config) => { // loading/token console.log("全局请求成功的拦截"); return config; }, (err) => { console.log("全局请求失败的拦截"); return err; } ); this.instance.interceptors.response.use( (res) => { console.log("全局响应成功的拦截"); return res; }, (err) => { console.log("全局响应失败的拦截"); return err; } ); } // 2. 封装网络请求的方法 request(config: AxiosRequestConfig) { return this.instance.request(config); } get() {} } export default LiliRequest;
- 针对特定的请求做拦截器
import axios from "axios"; import type { AxiosInstance, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from "axios"; // 拦截器:蒙版 loading/token/修改配置 import type { LILIInterceptors, LiliRequestConfig } from "./type"; class LiliRequest { instance: AxiosInstance; // 1. 创建实例 // request 实例 => axios实例 constructor(config: LiliRequestConfig) { this.instance = axios.create(config); // 每个instance实例都添加拦截器 // 传入一个config对config进行修改再返回 this.instance.interceptors.request.use( (config) => { // loading/token console.log("全局请求成功的拦截"); return config; }, (err) => { console.log("全局请求失败的拦截"); return err; } ); this.instance.interceptors.response.use( (res) => { console.log("全局响应成功的拦截"); return res; }, (err) => { console.log("全局响应失败的拦截"); return err; } ); // 同时存在不存在覆盖 // 判断请求是否添加拦截器,针对每一个请求做精细化处理 // 针对特定的liliRequest实例添加拦截器 // if (config.interceptors) {//类型缩小 this.instance.interceptors.request.use(config.interceptors?.requestSuccessFn, config.interceptors?.requestFailureFn); this.instance.interceptors.response.use(config.interceptors?.responseSuccessFn, config.interceptors?.responseFailureFn); // } } // 2. 封装网络请求的方法 request(config: AxiosRequestConfig) { return this.instance.request(config); } get() {} } export default LiliRequest;
- LiliRequest 类单次请求:针对不同的需求创建不同的拦截
import axios from "axios"; import type { AxiosInstance, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from "axios"; // 拦截器:蒙版 loading/token/修改配置 import type { LILIInterceptors, LiliRequestConfig } from "./type"; class LiliRequest { instance: AxiosInstance; // 1. 创建实例 // request 实例 => axios实例 constructor(config: LiliRequestConfig) { this.instance = axios.create(config); // 每个instance实例都添加拦截器 // 传入一个config对config进行修改再返回 this.instance.interceptors.request.use( (config) => { // loading/token console.log("全局请求成功的拦截"); return config; }, (err) => { console.log("全局请求失败的拦截"); return err; } ); this.instance.interceptors.response.use( (res) => { console.log("全局响应成功的拦截"); return res; }, (err) => { console.log("全局响应失败的拦截"); return err; } ); // 同时存在不存在覆盖 // 判断请求是否添加拦截器,针对每一个请求做精细化处理 // 针对特定的liliRequest实例添加拦截器 // if (config.interceptors) {//类型缩小 this.instance.interceptors.request.use(config.interceptors?.requestSuccessFn, config.interceptors?.requestFailureFn); this.instance.interceptors.response.use(config.interceptors?.responseSuccessFn, config.interceptors?.responseFailureFn); // } } // 2. 封装网络请求的方法 request(config: LiliRequestConfig) { // 拦截器的本质是一些钩子函数,对其进行回调 if (config.interceptors?.requestSuccessFn) { config = config.interceptors.requestSuccessFn(config); } return new Promise((resolve, reject) => { this.instance .request(config) .then((res) => { if (config.interceptors?.responseSuccessFn) { res = config.interceptors.responseSuccessFn(res); } resolve(res); }) .catch((err) => { reject(err); }); }); } get() {} } export default LiliRequest;
3.2 返回结果的类型处理
- 返回结果是一个 unknown 类型
- 网络请求
import liliRequest from ".."; interface IHmoeData{ data: any, returnCode: string, success:boolean } liliRequest.request<IHmoeData>({ url:"/user/name" }).then(res => { console.log(res); })
- 封装类
// 2. 封装网络请求的方法 request<T=any>(config: LiliRequestConfig) { // 拦截器的本质是一些钩子函数,对其进行回调 if (config.interceptors?.requestSuccessFn) { config = config.interceptors.requestSuccessFn(config); } return new Promise<T>((resolve, reject) => { this.instance .request<any,T>(config) .then((res) => { if (config.interceptors?.responseSuccessFn) { // res = config.interceptors.responseSuccessFn(res); } resolve(res); }) .catch((err) => { reject(err); }); }); }
3.3 接口类型中的泛型处理
- 封装类
import axios from "axios"; import type { AxiosInstance, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from "axios"; // 拦截器:蒙版 loading/token/修改配置 import type { LILIInterceptors, LiliRequestConfig } from "./type"; class LiliRequest { instance: AxiosInstance; // 1. 创建实例 // request 实例 => axios实例 constructor(config: LiliRequestConfig) { this.instance = axios.create(config); // 每个instance实例都添加拦截器 // 传入一个config对config进行修改再返回 this.instance.interceptors.request.use( (config) => { // loading/token console.log("全局请求成功的拦截"); return config; }, (err) => { console.log("全局请求失败的拦截"); return err; } ); this.instance.interceptors.response.use( (res) => { console.log("全局响应成功的拦截"); return res.data; }, (err) => { console.log("全局响应失败的拦截"); return err; } ); // 同时存在不存在覆盖 // 判断请求是否添加拦截器,针对每一个请求做精细化处理 // 针对特定的liliRequest实例添加拦截器 // if (config.interceptors) {//类型缩小 this.instance.interceptors.request.use(config.interceptors?.requestSuccessFn, config.interceptors?.requestFailureFn); this.instance.interceptors.response.use(config.interceptors?.responseSuccessFn, config.interceptors?.responseFailureFn); // } } // 2. 封装网络请求的方法 request<T=any>(config: LiliRequestConfig<T>) { // 拦截器的本质是一些钩子函数,对其进行回调 if (config.interceptors?.requestSuccessFn) { config = config.interceptors.requestSuccessFn(config); } return new Promise<T>((resolve, reject) => { this.instance .request<any,T>(config) .then((res) => { if (config.interceptors?.responseSuccessFn) { // res = config.interceptors.responseSuccessFn(res); } resolve(res); }) .catch((err) => { reject(err); }); }); } get() {} } export default LiliRequest;
- 修改类型
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from "axios"; // 针对AxiosRequestConfig配置进行扩展 export interface LILIInterceptors<T=AxiosResponse> { requestSuccessFn?: (config: AxiosRequestConfig) => AxiosRequestConfig; requestFailureFn?: (err: any) => any; responseSuccessFn?: (res: T) => T; responseFailureFn?: (err: any) => any; } export interface LiliRequestConfig<T=AxiosResponse> extends AxiosRequestConfig { interceptors?: LILIInterceptors<T>; }
3.4 添加其他请求方式
get<T = any>(config: LiliRequestConfig<T>) { return this.request({...config,method:"GET"}) } post<T = any>(config: LiliRequestConfig<T>) { return this.request({...config,method:"POST"}) } delete<T = any>(config: LiliRequestConfig<T>) { return this.request({...config,method:"DELETE"}) } patch<T = any>(config: LiliRequestConfig<T>) { return this.request({...config,method:"PATCH"}) }
3.5 总结
- 拦截器进行精细控制
- 全局拦截器
- 实例拦截器
- 单次请求拦截器
- 响应结果的类型处理