^_^ 基本介绍
- Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。
- 应用场景: 像VUE、react、Node等项目。
- 评价: 可以说axios把请求这件事做到了极致,封装的很好用,除了低版IE外,浏览器的支持性很好。
在Vue1.x中,官方推荐使用的ajax库是vue-resource。到了Vue2.x,官方(尤大)推荐的ajax库改为了Axios,其说法是因为已有一个更完备的轮子,就不需要造一个新的。 - 下载:
使用npm: npm i axios
使用cdn: <script src=“https://unpkg.com/axios/dist/axios.min.js”>
同类比对
- ajax:
优点:局部更新;原生支持
缺点:嵌套回调 - jqueryAjax:
在原生的ajax的基础上进行了封装;支持jsonp - fetch:
优点:解决回调地狱
缺点:API 偏底层,需要封装;默认不带Cookie,需要手动添加; 浏览器支持情况不是很友好,需要第三方的ployfill - axios:
直呼666.
^_^ 配置对象
小栗子:
let configObj = {
method: 'get',
url: 'https://www.baidu.com/s',
params: {
is: 'UTF-8',
wd: 'axios',
},
}
axios(configObj).then(res => {
console.log(res)
}).catch(rej => {
console.warn(rej)
})
说明:
以上发送的get请求,请求地址:https://www.baidu.com/s?ie=UTF-8&wd=axios
以例可见 配置对象 是axios的主要内容 和 请求的本质。
请求配置 的 常用属性
- method:
指定请求方法,默认为get请求。
- baseURL:
将自动加在
url
前面,除非url
是一个绝对 URL。
一般用在 axios实例中 作为提取 请求地址的公共部分使用。
- url:
请求地址,可以在其后面直接写请求参数,也可以在下面的params中写。
- params:
get请求的 请求参数,将与请求一起发送的 URL 参数。
params必须是一个无格式对象(plain object),所谓的无格式的对象就是属性值皆为字符串
的对象
- paramsSerializer:
是一个负责
params
序列化的函数,什么时候 params 需要序列化?
当params不是一个 无格式对象的时候,就需要序列化,否则无法拼接在url后面。paramsSerializer: function(params) { return Qs.stringify(params, {indices: false}) },
当params的属性值为一个数组时,就不再是无格式对象了:
params = {ids: [1, 2, 3]}
- qs.stringify({ids: [1, 2, 3]}, { indices: false })
序列化之后: ids=1&ids=2&id=3- qs.stringify({ids: [1, 2, 3]}, {arrayFormat: ‘indices‘})
序列化之后:ids[0]=1&aids1]=2&ids[2]=3- qs.stringify({ids: [1, 2, 3]}, {arrayFormat: ‘brackets‘})
序列化之后:ids[]=1&ids[]=2&ids[]=3- qs.stringify({ids: [1, 2, 3]}, {arrayFormat: ‘repeat‘})
序列化之后:ids=1&ids=2&id=3
- data:
post、put、patch 请求的 请求体。
Vue使用axios请求数据,默认post请求传参是json格式
,若需要formData格式,如此操作:// 安装axios时,qs也被axios裹挟安装了 import qs from 'qs' let reqObj = {...} let formDataObj = qs.stringify(reqObj) let configObj = { data: formDataObj, headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }
多说一嘴,curl 的 POST的 -d 的请求体 默认的是 formData格式。
- transformRequest:
在向服务器发送前,修改post、put、patch 请求的 请求体.
建议用在 axios实例中 作为提取 请求地址的公共部分使用。let configObj = { transformRequest: [function (data) { // 对 data 进行任意转换处理 ...... // 必须将处理后的 结果 送出来 // 且 返回的结果 必须一个字符串,或 ArrayBuffer,或 Stream return JSON.stringify(data); }], // 经过 transformRequest 加工,data从对象变成了字符串, // 所以在请求头中说一下 这个 字符串 其本质 是 一个 json对象。 headers: { 'Content-Type': 'application/json', } }
- transformResponse:
在 响应数据 传递给 then/catch 前,可以在此 进行修改.
在这里获取的 数据,是后台传过来的res.data数据
(响应体中的数据),
且是 数据是 原始类型,即 json字符串,
为什么在then中获取到的确实res.data 是对象类型,
这是因为 axios 内部给自动解析处理,这点也就是作者说的特性之一:自动转换json数据。
像 fetch 请求 就需要手动 将后端传过来的json字符串解析成对象let configObj = { method: 'post', transformResponse: [function (responseData){ let data = JSON.parse(responseData) data.name= 'wtt' return data }], }
- timeout:
指定请求超时的
毫秒数
(0 表示无超时时间)
如果请求话费了超过timeout
的时间,请求将被中断
- headers:
配置请求报文的 请求头 对象。
通过 请求头 传递 token, 可以在请求头中自定义一个Token字段let configObj = { headers: { 'Token': '123123123' } }
来,大概了解一下请求头相关 字段
- Accept: 浏览器可以接收的内容类型
- Content-Type:请求体中的内容的mime类型。
- Accept-Charset:浏览器能识别的字符集
- Accept-Encoding:浏览器可以处理的编码方式
- Accept-Language:浏览器接收的语言
- Authorization:: HTTP 基础验证,并提供凭据
- Cache-Control::指示缓存系统应该怎样处理缓存
- Connection:告诉服务器使用怎样的连接方式。有keep-alive和close。
- Cookie::发送请求时发送cookie
- Content-Length:一个请求的请求体的内存长度,单位为字节(byte)。
- Content-MD5:使用base64进行了编码的请求体的MD5校验
- Date:发送请求时的GMT时间。
- Referer:指当前请求的 来自。
- User-Agent:通常就是用户的浏览器相关信息。
- Via:用来记录一个请求经过了哪些代理或网关才被送到目标服务器上。
- …
- auth
是 HTTP 基础验证,并提供凭据,
该处的设置,会覆写掉用headers
设置的自定义Authorization
头
在这里主要写一些验证相关的信息,例如 token,token写在这里 显得 专业,
如果像上面非要在 header中 自定义一个 Token字段也可以 但是要和 后端的同学 说一下。let configObj = { auth: { 'token': '123123123' } }
vue 项目中使用axios 的 跨域
配置
- 跨域问题 是由于 浏览器 本身的一种安全机制所导致的,也就是说只用浏览器和服务器之间会存在 跨域 的问题,注意 本机测试 跨域问题,即使是localhost也要带上 http 前缀,否则 浏览器不会发出请求 。
- jsonp 是解决 跨域问题的一种方案,利于标签的scr属性不受跨域限制的特性(凡是拥有”src”这个属性的 标签 都拥有跨域的能力,比如script、img)。
- 代理 是解决 跨域的另一种方案。
原理解释:上面说过 只有 浏览器发出的请求 存在这跨域的问题,也就是说 服务器 和 服务器 之间是没有跨域的概念(如果服务器没有设置禁止跨域的权限问题), 可以相互请求数据, 因此 配置一个代理的服务器 去请求 目标服务器中的数据,然后再把请求的响应数据返回给客户端,这样就可以实现跨域访问数据。
对于axios而言,只能使用get和post方法来进行请求数据,不同通过标签的src属性进行数据访问,所以axios 解决跨域的方案只能是 使用代理了。
Talking is cheap, show me the code!:
- 在vue根目录下创建:
vue.config.js
文件,在项目编译 的时候 这个文件会和 隐藏起来的那一大堆vue create 项目
初始化形成的配置文件 自动进行合并、覆盖。 - 在配置文件中写入 代理
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://qqfwwx.tidaizhe.cn:8036', //后端服务器域名
changeOrigin: true, //是否跨域
pathRewrite: {
'^/api': ' ' //正则匹配 /api =>路径重写
}
}
},
}
}
- 更改 axios 的 baseURL
原先:
axios({
baseUrl: ' http://qqfwwx.tidaizhe.cn:8036/bbb/ ',
url: ' /aaa ',
})
更改之后:
axios({
baseUrl: ' /api/bbb/ ',
url: ' /aaa ',
})
- 重新跑一下项目(只要是动了 vue.config.js中的内容)
说明:
并不是说,如果设置了代理,axios发送的每个请求就必须都得使用代理,
不是这样的,如果我们有些请求不想使用代理 发送请求,
就想在本地服务器的url下 发出请求,那么就不写含有代理意义的 /api 的字段,
请求的url 原先怎么写就怎么写就可以了。
^_^ axios 的常用 API
axios(url[, config]) 默认使get请求
axios.get
(url[, config])
axios.post
(url[, data[, config]])
axios.put
(url[, data[, config]])
axios.delete
(url[, config])
axios.patch
(url[, data[, config]])
说明:
在使用别名时
, url、method、data 这三个属性 不必出现在 配置对象 中。
处理并发请求的助手函数: axios.all(); axios.spread()
let axios = require('axios')
let configObj111 = {
method: 'post',
url: 'https://jsonplaceholder.typicode.com/posts',
data: {
a: 11111,
},
}
let configObj222 = {
method: 'post',
url: 'https://jsonplaceholder.typicode.com/posts',
data: {
a: 22222,
},
}
axios.all([
axios(configObj1),
axios(configObj2),
]).then(axios.spread((res1,res2) => {
console.log(res1.data,111111)
console.log(res2.data,222222)
}))
请求实例最大的价值就是 给众多 请求 按照 公共域名后缀 进行分类处理,
抽离每一类的公共部分 作为 请求实例 的内容。
// 抽离一类请求的公共配置
let publicConfig = {
baseURL: 'http://www.baidu.com/api',
timeout: 5000,
}
let axiosInstance = axios.create(publicConfig)
let configObj = {
method: 'get',
url: '/aaa/bbb',
params: {
name: 'tom',
},
}
axiosInstance(configObj).then(res => {
console.log(res)
})
为了方便操作,请求实例 也为支持请求的方法提供了 别名api
axiosInstance.
get
(url[, config])
axiosInstance.post
(url[, data[, config]])
axiosInstance.put
(url[, data[, config]])
axiosInstance.delete
(url[, config])
axiosInstance.patch
(url[, data[, config]])
栗子:
let publicConfigObj = {
baseURL: 'https://jsonplaceholder.typicode.com',
}
let instance = axios.create(publicConfigObj)
const configObj = {
timeout: 5000,
}
let data={name: 'wtt'}
instance.post('/posts', data, configObj).then(res => {
console.log(res)
}).catch(rej => {
console.warn(rej)
})
注意: 请求实例 的请求 不要用 并发相关的api
通过 axios 的 defaults
属性 设置全体 axios 的公共配置
let axios = require('axios')
axios.defaults.baseURL = 'https://api.example.com';
说明:
请求实例 也是可以 通过 defaults
属性 设置 实例默认值的,但是很少用。
配置 的 优先级
具体请求 的 配置对象 的优先级 最大
;
请求实例 的 配置对象 的优先级 其次
;
全局默认值 的 优先级 最小
。
请求实例 也可以 设置拦截器。
// use 两个参数函数:
// 第一个: 请求成功
// 第二个: 请求失败
axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
......
return config; // 放行
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
说明:
前面在介绍 配置对象 时 有提到 transformRequest,注意区分,
transformRequest 是数据拦截,其的权限 仅是 对于 请求体 数据的修改,
而这里的 请求拦截 是对本次请求 的更改,直白的说 可以修改 配置对象
。
栗子:
let axios = require('axios')
// 这里我 只用 第一个参函数
axios.interceptors.request.use(function (config) {
// 修改 配置对象
if (config.timeout < 100) {
console.log("时间太短了吧,干脆别请求了")
let rej = "给 catch 说, 我把请求取消了"
return Promise.reject(rej) // 可简写: throw rej
} else {
console.log("时间这还差不多,我再改一下时间 就更好玩了")
config.timeout = 65535
return config
}
});
let arr1 = 'https://jsonplaceholder.typicode.com/posts'
let arr2 = { a:1111 }
let arr3 = { timeout: 20 }
axios.post(arr1, arr2, arr3).then(res => {
console.log(res.config,'then 捕获到的')
}).catch(rej => {
console.warn(rej,'catch 捕获到的')
})
/* 输出:
时间太短了吧,干脆别请求了
给 catch 说, 我把请求取消了 catch 捕获到的
*/
上面 请求拦截 里面的相关说明,这里一样适用。
axios.interceptors.response.use(function (response) {
// 对响应数据做点什么
return response; // 放行
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error);
});
栗子:
let axios = require('axios')
axios.interceptors.response.use(function (res) {
if (res.status == 200) {
let ok = "给 then 说,请求成功了"
return ok
} else {
let fail = "给 catch 说,请求失败了"
throw fail
}
});
let arr1 = 'https://jsonplaceholder.typicode.com/posts'
let arr2 = {a: 123}
axios.post(arr1, arr2).then(res => {
console.log(res,'then 捕获到的')
}).catch(rej => {
console.warn(rej,'catch 捕获到的')
})
/* 输出:
当 res.status == 200的输出: 给 then 说,请求成功了 then 捕获到的
当 res.status != 200的输出: 给 catch 说,请求失败了 catch 捕获到的
*/
^_^ axios 请求返回 的 数据结构
// 由5部分构成
{
// 响应体的数据
data: {},
// 状态码
status: 200,
// 状态信息
statusText: 'OK',
// 响应头
headers: {},
// 请求 的 配置对象
config: {}
}
^_^ 项目中 axios模块封装
- request.js
import axios frim 'axios'
axios.defaults.btimeout = 6666
// 根据项目 需要 设置 请求拦截器 和 响应拦截器
export const instance111 = axios.create({
baseURL: "http://www.baidu.com/aaa"
})
export const instance222 = axios.create({
baseURL: "http://www.baidu.com/BBB"
})
- home.vue
import {instance111} from "./request.js"
function getRequest(configObj) {
instance111(configObj).then(res => {
console.log(res)
}).catch(rej => {
console.log(rej)
})
}
说明:
如果 请求过多 可以 将请求 抽离 出一个 home.js文件。
^_^ axios 上传文件
<template>
<input type="file" @change="Upload" />
</template>
<script>
import axios from "axios";
export default {
methods: {
Upload(event) {
let file = event.target.files[0];
// 在这里进行一系列的校验
let formData = new FormData();
formData.append("avatar", file);
formData.append("name", "wtt");
axios.post("http://localhost:8090/upload", formData, {
"Content-type": "multipart/form-data",
})
.then(res => {
console.log(res)
});
},
},
};
</script>
FormData 类
上面axios上传文件时,把文件数据放到了 FormData实例中,下面说以下js中的FormData 类 的基本用法。
FormData可以将form表单元素的name与value进行组合(var formData=new FormData(form);),实现表单数据的序列化,从而简化表单元素的拼接,提高工作效率。
- FormData.append()
向 FormData 中添加新的属性值,如果FormData 对应的属性值存在则覆盖原值,否则新增一项属性值。- FormData.set()
给 FormData 设置属性值,如果FormData 对应的属性值存在则覆盖原值,否则新增一项属性值- FormData.get()
返回在 FormData 对象中与给定键关联的第一个值- FormData.getAll()
返回一个包含 FormData 对象中与给定键关联的所有值的数组。- FormData.delete()
从FormData对象里面删除一个键值对- FormData.has()
返回一个布尔值表明 FormData 对象是否包含某些键- FormData.keys()
返回一个包含所有键的iterator对象 ,通过 for…of 查看返回的数据- FormData.values()
返回一个包含所有值的iterator对象 ,通过 for…of 查看返回的数据- FormData.entries()
返回一个包含所有键值对的iterator对象,通过 for…of 查看返回的数据
^_^ axios 下载文件
// 响应式显示 下载进度
let finish = ref(0)
// 文件下载-----------------------------
const videoDownloadLocal = (url) => {
let downLoadFileName = "自定义下载的文件名.mp4";
axios({
method: "get",
url,
// 必须显式指明响应类型是一个Blob对象,这样生成二进制的数据,才能通过window.URL.createObjectURL进行创建成功
responseType: "blob",
// 过程事件监听
onDownloadProgress(e) {
let percent = Math.floor(100 * (e.loaded / e.total));
console.warn(percent)
finish.value = percent
},
}).then((res) => {
if (!res) {
return;
}
console.log(res);
// 将lob对象转换为域名结合式的url
let blobUrl = window.URL.createObjectURL(res.data);
let link = document.createElement("a");
document.body.appendChild(link);
link.style.display = "none";
link.href = blobUrl;
// 设置a标签的下载属性,设置文件名及格式,后缀名最好让后端在数据格式中返回
link.download = downLoadFileName;
// 自触发click事件
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(blobUrl);
/*
至此下载完成-------------------------------------------
*/
}).catch((rej) => {
console.error(rej)
})