客户端向服务器端请求数据主要有ajax,axios,fetch方法。ajax是最原始的方法,而fetch是浏览器内置的api,是请求的另一种方式,它相对于ajax的最大好处之一就是天生是基于Promise原理的,可以实现异步请求。而axios是基于ajax和Promise封装的库。这次简要谈下ajax的使用,主要记录axios的二次封装。
@TOC
一、ajax发送请求的步骤
1.创建XMLHttpRequest实例对象
代码如下:
var XMLHttpRequest = new XMLHttpRequest();
2.创建HTTP 请求
代码如下:
XMLHttpRequest.open(method,URL,async,username,password)
- method :用于请求的 HTTP 方法。包括 GET 、POST、HEAD、PUT、DELETE(不区分大小写)
- url :请求的路径。
- async :指示请求使用应该异步执行。如果这个参数为 false,代表请求是同步的,后续对 send() 的调用将阻塞,直到响应完全接受;如果这个参数是 true 或省略,请求是异步的,且通常需要一个 onreadystatechange 事件句柄
- username 和 password :可选。
3.发送HTTP请求
使用 XMLHttpRequest .send() 来发送请求。
XMLHttpRequest.send(data)
// 其中data是个可选参数,如果请求的数据不需要参数,即可以使用null来替代。
4.接收返回的数据——设置响应HTTP 请求状态变化的函数
从创建XMLHttpRequest对象,到发送数据、接收数据,一共会经历5种状态。对应的readyState属性值为0,1,2,3,4。当请求被发送到服务器且响应数据时,需要执行一些基于响应的操作,这些操作在onreadystatechange事件中执行,每当readyState改变时,就会触发onreadystatechange事件。
- 0 刚创建XMLHttpRequest对象,对象处于未初始化状态。
- 1: 对象使用open()方法创建了HTTP请求,对象处于初始化状态。
- 2: 对象使用send()方法发送数据,对象处于发送数据状态。
- 3: 请求处理中,响应中已有部分数据生产。XMLHttpRequest对象处于接收数据状态。
- 4: 响应已完成,可以获取并使用响应中的数据。比如responseText属性或responseXml属性。
要想从服务器端获得返回的数据,就必须先判断 XMLHttpRequest 对象的状态。在函数里判断 XMLHttpRequest 对象的 readyState 值,如果readyState === 4 ,表示异步调用过程完毕,则使用 responseText 属性或 responseXml 属性来获取数据,但是异步调用过程完毕,并不代表异步调用成功,还要判断 XMLHttpRequest 对象的status属性值,只有status === 200 ,才表示异步调用成功。
注意:若HTML文件不是在Web 服务器上运行,而是在本地运行,则 xmlHttpRequest.status 的返回值为 0。 代码如下:
XMLHttpRequest.onreadystatechange = getData;
function getData(){
if(xmlHttpRequest.readyState === 4){
if(xmlHttpRequest.status === 200 || xmlHttpRequest.status === 0 ){
// 获取数据的语句
document.write(xmlHttpRequest.responseText);
}
}
}
二、axios的二次封装
1. 普通axios的使用
安装axios 引入axios
import axios from 'axios'
axios.get(url,{
params:{
name:'wmh'
}
}).then(res=>{})
.catch(err=>{})
2. axios的二次封装
封装原因:把公共部分都提前封装,以后每次使用直接调用封装好的库,操作起来更加方便。
(1)先准备好接口—使用mock来模拟生成数据
在mock在线网站注册登录并创建项目(此处创建的是登录页面的后台数据)
写好接口后点击保存,再将上面的mock接口根地址复制,以备后续使用。
(2)根据不同的环境变量来定义不同的请求接口
在真正的项目开发中,一般是会分环境来请求接口的,一般有3个分别为开发环境,测试环境和生产环境。(在vite构建的vue3项目中),在src目录下创建一个config文件夹,在文件夹下创建index.js文件,在里面设置3种环境变量,每种环境请求对应的接口,mock地址统一。并导出项目所处的环境变量。vite创建的vue项目中,获取环境变量要用import.meta.env。 代码如下: config—index.js
// 环境配置封装
// 获取环境变量
// 定义不同的环境中的api地址
const env = import.meta.env.MODE || 'prod'
const EnvConfig = {
dev:{//开发环境调用的api
baseApi:'/',
mockApi:'https://www.fastmock.site/mock/756115e95fd471a83e21dd95ac29bcd6/api'
},
test:{//测试环境调用的api
baseApi:'//test.futurefe.com/api',
mockApi:'https://www.fastmock.site/mock/756115e95fd471a83e21dd95ac29bcd6/api'
},
prod:{//生产环境调用的api
baseApi:'//futurefe.com/api',
mockApi:'https://www.fastmock.site/mock/756115e95fd471a83e21dd95ac29bcd6/api'
}
}
//导出环境变量
export default{
env,
mock:true,
...EnvConfig[env]//解构
}
(3)创建实例添加配置
在src目录下创建一个utils目录,在目录下创建一个index.js文件。创建一个axios实例,并添加全局配置。
utils—request.js
import axios from "axios";
import config from "../config";
import qs from 'qs'
//创建axios实例对象,添加全局配置
const serve = axios.create({
baseURL:config.baseApi,
timeout:8000
})
//按需配置,还可以配置其他的,比如跨域是否允许携带凭证以及传递的数据格式(看服务器要求什么格式)
//serve.default.withCredencials = true
//serve.default.headers['Content-Type']='application/x-www-form-urlencoded'
//若是post请求,还能用transformRequest来转换数据的格式,transformRequest只对post有效。
//serve.default.transformRequest = data => qs.stringify(data)
//qs是第三方库
//qs.stringify(data)将对象序列化为url格式xxx=xxx&xxx=xxx...
//qs.parse(data)将url解析成对象
(4)设置请求拦截器
客户端发送请求——请求拦截器——服务器端 utils—request.js
serve.interceptors.request.use((req)=>{
//TO-DO
const head = req.headers
if(!head.Authorization) head.Authorization = "Bear jack"
return req
})
请求拦截器拦截可以对请求头添加配置,再返回给服务器。
(5)设置响应拦截器
服务端响应数据——响应拦截器——客户端接收 成功状态:直接返回数据 失败状态: 服务器返回失败信息:可能是登录异常(根据不同的状态码做相应处理),抛出异常 服务器连信息都没返回:(服务端出错)提示网络异常
utils—request.js
//响应拦截
import axios from "axios";
import config from "../config";
import { ElMessage } from "element-plus";
import router from "../router";
const TOKEN_INVALID = 'Token认证失败,请重新登录'
const NETWORK_ERROR = '网络请求异常,请稍后重试'
serve.interceptors.response.use((res)=>{
//TO-DO
const {code,data,msg} = res.data
//成功
if(code===200){
return data
}
//登录报错
else if(code === 401){
ElMessage.error(TOKEN_INVALID)
//捕获到错误给点时间再跳转页面
setTimeout(()=>{
router.push('/login')
},15000)
//用Promise.reject来抛出异常
return Promise.reject(TOKEN_INVALID)
}
else{
ElMessage.error(msg||NETWORK_ERROR)
return Promise.reject(msg||NETWORK_ERROR)
}
})
(6)设置请求的核心函数
function request(options){
//请求有指明请求方法则使用请求方法否则默认为'get’请求
options.method = options.method || 'get'
if(options.method.toLowerCase() === 'get'){//大小写
options.params = options.data
}
//生产环境下防止其调用mock的api
if(config.env === 'prod'){
serve.defaults.baseURL = config.baseApi
}
else{
serve.defaults.baseURL = config.mock?config.mockApi:config.baseApi
}
return serve(options)
}
(7)导出封装好的对象
export default request
(8)在main.js中配置全局挂载对象
main.js中添加以下代码
import request from './utils/request'
//挂载全局对象:可全局使用
app.config.globalProperties.$request = request
(9)在login页面中验证
<script>
import Welcome from './Welcome.vue'
export default{
name:'login',
components:{Welcome},
mounted(){
this.$request({
method:'get',
url:'/login',
data:{
name:'jack'
}
}).then((res)=>{
console.log(res)
})
},
methods:{
goHome(){
this.$router.push('/welcome')
}
}
}
</script>
<template>
<div>
<h1>欢迎来到登录界面</h1>
<el-button @click="goHome">回首页</el-button>
</div>
</template>
<style>
</style>
添加的代码:
mounted(){
this.$request({
method:'get',
url:'/login',
data:{
name:'jack'
}
}).then((res)=>{
console.log(res)
})
}
//上述的options即$request内的内容。