前言 要了解axios,需要先了解Ajax(Asynchronous JavaScript And XML 即异步 JavaScript 和 XML)请求。简单来说,Ajax的作用为从服务端获取数据,实现页面的局部刷新。
一般请求和Ajax请求:
一般请求:浏览器会直接显示响应体数据,会自动刷新整个网页或跳转页面。
Ajax请求:浏览器不会对整个界面进行刷新,得到数据后,对部分的元素进行刷新。
显然,Ajax请求减少了页面频繁刷新的次数 ,使用者的体验更加丝滑。Axios也是用来发送Ajax请求。
Axios 是一个流行的基于 Promise 的 HTTP 请求库,用于在浏览器和 Node.js 中进行 HTTP 请求。它提供了简单易用的 API,可以发送各种类型的请求(如 GET、POST、PUT、DELETE等),并处理响应数据,Axios 在前端工程化项目中有 99% 的概率会被优先选择。
Axios中文文档 | Axios中文网 (axios-http.cn)
演示API文档 后续演示使用的api文档,api文档的制作可以参考apiDoc 。
模拟服务端使用的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 import Koa from "koa" ;import Router from "@koa/router" ;import Cors from "@koa/cors" ;import { bodyParser } from "@koa/bodyparser" ;const app = new Koa ();const hostname = "127.0.0.1" ;const port = 5000 ;app.use (Cors ()); app.use (bodyParser ({ parsedMethods : ["POST" , "PUT" , "PATCH" , "DELETE" , "GET" ], })); let idList = ["0957c786" , "00ad48e5" , "a80ee35a" , "14bff5f7" ];let students = [ { id : "d93d5860" , name : "Alice" , age : 18 }, { id : "a2f7d296" , name : "Bob" , age : 18 }, { id : "d28403fa" , name : "Charlie" , age : 20 }, { id : "2acbcc70" , name : "David" , age : 19 }, ]; const router = new Router ();router.get ("/persons" , async (ctx) => { const res = { status : 1 , message : "success" , data : students, }; ctx.body = res; }); router.get ("/person" , async (ctx) => { const id = ctx.query .id ; console .log ("id:" , id); const student = students.find ((student ) => student.id === id); let res; if (student) { res = { status : 1 , message : "success" , data : student, }; } else { res = { status : 1 , message : "The student was not found" , }; } ctx.body = res; }); router.get ("/filter/:age" , async (ctx) => { const age = ctx.params .age ; const student = students.filter ((student ) => student.age === age); let res; if (student.length > 0 ) { res = { status : 1 , message : "success" , data : student, }; } else { res = { status : 1 , message : "The student was not found" , }; } ctx.body = res; }); router.post ("/person" , async (ctx) => { const body = ctx.request .body ; const id = idList[Math .floor (Math .random () * idList.length )]; idList.slice (idList.indexOf (id), 1 ); let student = { id : id, name : body.name , age : body.age , }; students.push (student); let res = { status : 1 , message : "add success" , data : student, }; ctx.body = res; }); router.put ("/person" , async (ctx) => { const body = ctx.request .body ; const id = body.id ; const student = students.find ((student ) => student.id === id); let res; if (student) { student.name = body.name ; student.age = body.age ; res = { status : 1 , message : "update success" , data : student, }; } else { res = { status : 0 , message : "student not found" , }; } ctx.body = res; }); router.delete ("/person" , async (ctx) => { const body = ctx.request .body ; const id = body.id ; const student = students.find ((student ) => student.id === id); let res; if (student) { students = students.filter ((student ) => student.id !== id); res = { status : 1 , message : "delete success" , data : id, }; } else { res = { status : 0 , message : "student not found" , }; } ctx.body = res; }); app.use (router.routes ()); app.use (async (ctx) => { if (!ctx.body ) { ctx.status = 404 ; ctx.body = "404 Not Found" ; } }); app.listen (port, hostname, () => { console .log (`Server running at http://${hostname} :${port} /` ); });
初始数据为
1 2 3 4 5 6 [ { id: "d93d5860" , name: "Alice" , age: 18 } , { id: "a2f7d296" , name: "Bob" , age: 18 } , { id: "d28403fa" , name: "Charlie" , age: 20 } , { id: "2acbcc70" , name: "David" , age: 19 } , ] ;
基本使用 引入cdn
1 <script src="https://cdn.bootcdn.net/ajax/libs/axios/1.7.5/axios.min.js" ></script>
创建一个页面,并获取所有人的数据。地址为/persons
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <title > Document</title > <script src ="https://cdn.bootcdn.net/ajax/libs/axios/1.7.5/axios.min.js" > </script > </head > <body > <h1 > axios的使用</h1 > <button onclick ="getData()" > 获取数据</button > <script > function getData ( ) { } </script > </body > </html >
axios有两种写法:
简写形式:axios.get(url)
一般形式:axios({method:"GET",url:url})
1 2 3 4 5 6 7 8 9 10 async function getData ( ) { let result = await axios.get ("http://127.0.0.1:5000/persons" ) let result = await axios ({ method : 'get' , url : 'http://127.0.0.1:5000/persons' }) }
axios返回的是一个Promise对象(也可以使用.then()和.catch()的写法,推荐异步async写法),其中包含请求的很多数据,我们需要的数据在data字段中。
按照文档(也可参照上图),服务器返回的内容也是一个对象,其中包含多种信息,真实的数据在返回对象的data字段中。所以,如果要获取到真实数据,需要使用result.data.data
1 2 3 4 5 6 7 8 9 10 11 12 13 async function getData ( ) { let result = await axios ({ method : 'get' , url : 'http://127.0.0.1:5000/persons' }) console .log ("响应成功:" ,result.data .data ); }
这样就可以得到真实的数据。
拦截器 拦截器是用来对请求和响应进行过滤、预处理的操作函数。具体原理图如下所示
请求拦截器 考虑到api文档中所有的请求都以http://127.0.0.1:5000作为开头,因此可以考虑用请求拦截器优化这个行为。
参考上文拦截器的原理示意图,使用拦截器interceptors时,我们需要传入本次请求的信息config,并需要从拦截器中返回处理后的config,否则就会请求错误。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 axios.interceptors .request .use ((config ) => { config.baseURL = 'http://127.0.0.1:5000' return config; }) async function getData ( ) { let result = await axios.get ('/persons' ) console .log (result); console .log ("响应成功:" ,result.data .data ); }
使用baseURL设置后,所有的请求都会自动加上这个前缀,避免了多次重复书写的麻烦。
请求拦截器config的设置,可以在请求配置 中找到。
响应拦截器 每次获取到数据都需要result.data.data,第一个data是由于axios本身造成的,且每次操作都一致,因此考虑使用相应拦截器来优化。
严格来说,请求是没有失败一说的,但是响应是有可能失败的。对与响应拦截器,还提供了一个回调函数供给出错处理使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 axios.interceptors .response .use ( (res )=> { console .log ("请求成功" ); return res.data }, (err )=> { console .log ("请求失败" , err); return err } ) async function getData ( ) { let result = await axios.get ('http://127.0.0.1:5000/persons' ) console .log ("响应成功:" , result.data ); }
这样处理后,每次只需根据api文档的要求获取相应对象的字段即可,展示在上述代码中即为只需写一个data。
另外,使用await关键字有一个弊端,如果响应失败,且在拦截器中返回了具体的内容的话,那么会出现完全矛盾的输出。修改请求为/person2(实际并不存在这个api),控制台得到如下的结果。
即便是响应失败了,还是让最后打印数据的语句执行了,这说明await得到了一个非reject的回应(代码返回的是err),因此程序正常执行了下去。可想而知,需要在错误处理和返回值的地方下手解决。
最简单的方法就是不写错误响应的回调函数 ,这样await迟迟等不来结果,就会抛出Uncaught (in promise) 的错误。但是这种方法无法得到任何语义化的提示,因此不是很推荐。
接下来介绍三种常用的处理方法。
全局axios配置 除了使用相应拦截器来规定默认的请求条件,也可以通过修改axios的全局配置项来实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 axios.defaults .baseURL = 'http://127.0.0.1:5000' axios.defaults .timeout = 3000 axios.defaults .headers = { "Content-Type" :"application/json" } async function getData ( ) { let result = await axios ({ url :"/persons" , method :"get" }) console .log ("响应成功:" ,result); }
这样和前文中相应拦截器的效果是相同的,不同在于修改config的时机。
interceptors.request - 发出请求后在拦截器中修改,属事后修改。
axios.defaults - 在发出请求前就已经配置,属事前修改。
后续创建的axios对象的设置会继承axios.defaults的值(非引用)。
全局是针对只有一个axios的情况 ,新创建的axios不会因默认axios.default的修改受到影响。
请求类型和请求参数 常用的请求类型:
类型
常用领域
axios形式
GET
查
axios#get(url[, config])
POST
增
axios#post(url[, data[, config]])
PUT
改
axios#put(url[, data[, config]])
DELETE
删
axios#delete(url[, config])
请求参数类型:
query参数(查询字符串)
params参数(路径传参)
请求体参数(json编码,urlencoded编码)
GET请求 GET请求多用于查询场景。
query参数 以id获取数据为例子。地址为/person
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 async function getData ( ) { let result = await axios ({ url :'/person' , method :'get' , params :{ id :'d93d5860' } }) console .log ("响应成功:" ,result); }
需要注意的是,设置query参数的名称为params,这是因为params本身就有参数的意思,最终可以得到相应的结果
1 2 3 4 5 6 7 8 9 { "status" : 1 , "message" : "success" , "data" : { "id" : "d93d5860" , "name" : "Alice" , "age" : 18 } }
如果替换为不存在的id,那么不会返回data字段
1 2 3 4 { "status" : 1 , "message" : "The student was not found" }
使用配置项传参,实际上是axios自动将params和baseURL拼接好。在控制台可以看到具体的请求url。
params参数 以age获取数据为例子。地址为/filter/age
params参数也称为路径传参。需要注意的是,params传参不存在配置项的写法 。
1 2 3 4 5 6 7 8 9 10 11 12 13 async function getData ( ) { let result = await axios ({ url :"/filter/18" , method :"get" }) console .log ("响应成功:" ,result); }
得到的返回结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 { "status" : 1 , "message" : "success" , "data" : [ { "id" : "d93d5860" , "name" : "Alice" , "age" : 18 } , { "id" : "a2f7d296" , "name" : "Bob" , "age" : 18 } ] }
如果输入age匹配不到,不会返回data字段
1 2 3 4 { "status" : 1 , "message" : "The student was not found" }
POST请求 POST请求主要用于添加数据。以添加用户为例子,地址为/person。
使用的是请求体参数,有两种格式:
json格式 - {name:"强哥",age:28}(自动解析)
urlencoded格式 - "name=强哥&age=28"
简写形式
1 2 3 4 5 6 7 async function addData ( ) { let result = await axios.post ('/person' ,{name :"强哥" ,age :28 }) let result = await axios.post ('/person' ,"name=强哥&age=28" ) console .log ("添加成功:" ,result); }
完整形式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 async function addData ( ) { let result = await axios ({ url :"/person" , method :"post" , data :{ name :"强哥" , age :28 } }) console .log ("添加成功:" ,result); }
两种格式都能够成功添加
1 2 3 4 5 6 7 8 9 { "status" : 1 , "message" : "add success" , "data" : { "id" : "a80ee35a" , "name" : "强哥" , "age" : "28" } }
在控制台的负载面板中可以看到上传的请求体参数。
查看所有人,可以看到人数加1,且能够看到添加的数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 { "status" : 1 , "message" : "success" , "data" : [ { "id" : "d93d5860" , "name" : "Alice" , "age" : 18 } , { "id" : "0957c786" , "name" : "强哥" , "age" : "28" } ] }
使用json作为请求体参数时,由于json不属于简单请求Content-Type的范围(urlencoded在范围内),因此请求为复杂请求 ,如果服务端没处理好,很有可能会遇到跨域问题。如果请求体参数以json格式发送,在开发者工具的网络面板中,可以看到预检请求(OPTIONS),具体的原因与解决方法参考前端-跨域&解决方案 。
PUT请求 PUT请求主要用于更新数据。以修改信息为例。地址为/person
完整形式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 async function updateData ( ) { let result = await axios ({ url :"/person" , method :"put" , data :{ id :"d93d5860" , name :"强哥" , age :500 }, }) console .log ("修改成功:" ,result); }
简写形式
1 2 3 4 5 6 7 async function updateData ( ) { let result = await axios.put ('/person' ,{id :"d93d5860" ,name :"强哥" ,age :500 }) console .log ("修改成功:" ,result); }
控制台得到的返回为:
1 2 3 4 5 6 7 8 9 { "status" : 1 , "message" : "update success" , "data" : { "id" : "d93d5860" , "name" : "强哥" , "age" : 500 } }
获取所有人数据,证实已被修改。
1 2 3 4 5 6 7 8 9 10 11 12 { "status" : 1 , "message" : "success" , "data" : [ { "id" : "d93d5860" , "name" : "强哥" , "age" : 500 } , ] }
DELETE请求 PUT请求主要用于删除数据。以珊瑚信息为例。地址为/person。
简写形式
1 2 3 4 5 6 7 8 9 async function delData ( ) { let result = await axios.delete ('/person' ,{data :{id :"d93d5860" }}) let result = await axios.delete ('/person' ,{data :"id=d93d5860" }) console .log ("删除成功:" ,result); }
完整形式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 async function delData ( ) { let result = await axios ({ url :"/person" , method :"delete" , data :{ id :"d93d5860" }, }) console .log ("删除成功:" ,result); }
返回的结果如下
1 2 3 4 5 { "status" : 1 , "message" : "delete success" , "data" : "d93d5860" }
查看原始数据,发现确实少了id为d93d5860的数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 { "status" : 1 , "message" : "success" , "data" : [ { "id" : "a2f7d296" , "name" : "Bob" , "age" : 18 } , { "id" : "d28403fa" , "name" : "Charlie" , "age" : 20 } , { "id" : "2acbcc70" , "name" : "David" , "age" : 19 } ] }
创建axios 假设需要访问多个不同的api源。除了http://127.0.0.1:5000,我们还需要访问另一个热搜榜的apihttps://tenapi.cn/v2。那么就需要多个axios对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 const axios2 = axios.create ({ baseURL :"https://tenapi.cn/v2" , timeout :3000 , headers :{ "Content-Type" :"application/json" } }) axios.defaults .baseURL = "http://127.0.0.1:5000" async function getAllPerson ( ) { let result = await axios.get ("/persons" ) console .log (result); } async function getHotData ( ){ let result2 = await axios2.get ("/bilihot" ) console .log (result2.data .data ); } async function getZhiData ( ){ let result3 = await axios2.get ("/zhihuhot" ) console .log (result3.data .data ); }
上述代码创建了一个新的axios对象用于获取热搜数据,将其baseURL设置为https://tenapi.cn/v2,而保留原本的axios用于本地http://127.0.0.1:5000的请求。这样两者就不会发生冲突。
注意:
批量发送请求 可以将多个axios对象放在一个列表中,使用axios.all()一次性发出全部请求。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 axios.defaults .baseURL = 'http://127.0.0.1:5000' axios.defaults .timeout = 5000 const axios2 = axios.create ()axios2.defaults .baseURL = 'https://tenapi.cn/v2' async function getAllData ( ){ let result = await axios.all ([ axios ({ url : "/persons" , method : "get" }), axios ({ url : "/filter/18" , method : "get" }), axios2 ({ url : "/bilihot" , method : "get" }), ]); console .log (result); }
返回的是装有所有响应请求对象的数组,如下所示(为了展示删除了对象的一些属性)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 [ { "data" : { "status" : 1 , "message" : "success" , "data" : [ { "id" : "d93d5860" , "name" : "Alice" , "age" : 18 }, { "id" : "a2f7d296" , "name" : "Bob" , "age" : 18 }, { "id" : "d28403fa" , "name" : "Charlie" , "age" : 20 }, { "id" : "2acbcc70" , "name" : "David" , "age" : 19 } ] }, "status" : 200 , "statusText" : "OK" , "headers" : { "content-length" : "209" , "content-type" : "application/json; charset=utf-8" }, }, { "data" : { "status" : 1 , "message" : "The student was not found" }, "status" : 200 , "statusText" : "OK" , "headers" : { "content-length" : "50" , "content-type" : "application/json; charset=utf-8" }, }, { "data" : { "code" : 200 , "msg" : "success" , "data" : [ { "name" : "周杰伦败诉网易" , "url" : "https://search.bilibili.com/all?vt=36849326&keyword=%E5%91%A8%E6%9D%B0%E4%BC%A6%E8%B4%A5%E8%AF%89%E7%BD%91%E6%98%93&order=click" }, { "name" : "宿傩死亡" , "url" : "https://search.bilibili.com/all?vt=36849326&keyword=%E5%AE%BF%E5%82%A9%E6%AD%BB%E4%BA%A1&order=click" }, { "name" : "黑神话Steam通关率不到14%" , "url" : "https://search.bilibili.com/all?vt=36849326&keyword=%E9%BB%91%E7%A5%9E%E8%AF%9DSteam%E9%80%9A%E5%85%B3%E7%8E%87%E4%B8%8D%E5%88%B014%25&order=click" } ] }, "status" : 200 , "statusText" : "" , "headers" : { "content-type" : "text/html; charset=UTF-8" } ]
Axios.all()基于promise.all(),所有的都是成功的回调才会返回数据,如果有一个失败的回调,就会得到reject的状态,抛出Uncaught (in promise)错误。
出现Uncaught (in promise)的主要原因是Promise返回的reject状态没有被处理,系统自动抛出错误。解决方法可以参考上文响应拦截器的正文部分。