02-axios

7/29/2022

# 1. 初识axios

axios是什么?

  • 前端最流行的ajax请求库
  • react/vue官方都推荐使用axios发ajax请求
  • 文档: https://github.com/axios/axios

功能特点

  • 基于xhr + promise的异步ajax请求库
  • 浏览器端/node端都可以使用
  • 支持 Promise API
  • 拦截请求和响应
  • 转换请求和响应数据
  • 支持请求取消
  • 批量发送多个请求

axios请求方式

  • axios(config): 通用/最本质的发任意类型请求的方式
  • axios(url[, config]): 可以只指定url发get请求
  • axios.request(config): 等同于axios(config)
  • axios.get(url[, config]): 发get请求
  • axios.delete(url[, config]): 发delete请求
  • axios.post(url[, data, config]): 发post请求
  • axios.put(url[, data, config]): 发put请求
  • axios.defaults.xxx: 请求的默认全局配置
  • axios.interceptors.request.use(): 添加请求拦截器
  • axios.interceptors.response.use(): 添加响应拦截器
  • axios.create(config): 创建一个新的axios(它没有下面的功能)
  • axios. Cancel(): 用于创建取消请求的错误对象
  • axios. CancelToken(): 用于创建取消请求的token对象
  • axios.isCancel(): 是否是一个取消请求的错误
  • axios.all(promises): 用于批量执行多个异步请求
  • axios.spread(): 用来指定接收所有成功数据的回调函数的方法

常见的配置选项

  • 请求地址      url: '/user'
  • 请求类型      method: 'get'
  • 根求路径      baseURL: 'http://www.mt.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

# 2. 发送request请求

前端代码

<!-- <script src="../node_modules/axios/dist/axios.min.js"></script> -->

<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>

<script>
    // 利用axios发请求,得到的结果是一个promise
    // 要得到成功的结果,就是需要.then
    // 真实的数据是在data中,其它的数据,都是axios帮我们封装好的
    // let axRes = axios.request({
    //     url: "http://127.0.0.1:3000/get",
    //     method: "get"
    // })
    // axRes.then(res => {
    //     console.log("data:", res.data);
    // })

    axios.request({
        url: "http://127.0.0.1:3000/get",
        method: "get"
    }).then(res => {
        console.log("data:", res.data);
    })
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

后端代码

const Koa = require("koa");
const cors = require("koa2-cors");
const logger = require("koa-logger");
const Router = require("@koa/router");
const koaBody = require("koa-body");
const app = new Koa();
const router = new Router();

app.use(cors());
app.use(logger());
app.use(koaBody());

router.get("/get", async (ctx, next) => {
    ctx.status = 200;
    ctx.body = "hello axios";
});

app.use(router.routes())
router.allowedMethods();

app.listen(3000, () => {
    console.log("running in http://127.0.0.1:3000");
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 3. 发送get请求

<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>

<script>
    // axios.get("http://127.0.0.1:3000/get").then(res => {
    //     console.log(res.data);
    // })

    (async function() {
        let res = await axios.get("http://127.0.0.1:3000/get")
        console.log(res);
    })()
</script>
1
2
3
4
5
6
7
8
9
10
11
12

后端代码同上

# 4. 发送get请求并传参

前端代码

<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>

<script>
    (async function() {
        // query传参
        // let res = await axios.get("http://127.0.0.1:3000/get?a=1&b=2")
        // console.log(res.data);

        // params传参
        let res = await axios.get("http://127.0.0.1:3000/get", {
            params: {
                name: "wc",
                age: 18,
                address: "bj"
            }
        })
        console.log(res.data);
    })()
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

后端代码

const Koa = require("koa");
const cors = require("koa2-cors");
const logger = require("koa-logger");
const Router = require("@koa/router");
const koaBody = require("koa-body");
const app = new Koa();
const router = new Router();

app.use(cors());
app.use(logger());
app.use(koaBody());

router.get("/get", async (ctx, next) => {
    ctx.status = 200;
    ctx.body = ctx.query;
});

router.get("/get/:name/:age/:address", ctx => {
    ctx.status = 200;
    ctx.body = ctx.params;
})

app.use(router.routes())
router.allowedMethods();

app.listen(3000, () => {
    console.log("running in http://127.0.0.1:3000");
});
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

# 5. 发送post请求

前端代码

<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>
<!-- querystring -->
<script src="../node_modules/qs/dist/qs.js"></script>

<script>
    (async function() {
        // post传递参数
        // 默认是json格式传递 Content-Type: application/json;charset=UTF-8
        // let res = await axios.post("http://127.0.0.1:3000/post",{
        //     a:1,
        //     b:2,
        //     c:3
        // })
        // console.log(res.data);

        // post传递参数2
        // 设置请求头   axios配置请求头
        // axios 是一个对象,对象也叫实例
        // 默认情况下,我们可以使用人家提供给我们的实例,人家帮我们提供的实例,很多配置都是定死的
        // 我们能不能自己去创建一个实例?
        // 答:可以

        // 如果我们想自己去配置一个更加灵活的axios实例,或我需要向不同的服务器发请求?
        // 此时,我们就可以自己去创建axios实例
        let res = await axios.post("http://127.0.0.1:3000/post", qs.stringify({
            a: 1,
            b: 2
        }), {
            headers: {
                "Content-Type": "application/x-www-form-urlencoded"
            }
        })
        console.log(res.data);
    })()
</script>
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

后端代码

const Koa = require("koa");
const cors = require("koa2-cors");
const logger = require("koa-logger");
const Router = require("@koa/router");
const koaBody = require("koa-body");
const app = new Koa();
const router = new Router();

app.use(cors());
app.use(logger());
app.use(koaBody());

router.post("/post", async (ctx, next) => {
    ctx.status = 200;
    ctx.body = ctx.request.body;
});

app.use(router.routes())
router.allowedMethods();

app.listen(3000, () => {
    console.log("running in http://127.0.0.1:3000");
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 6. 其它

<script>
    // axios.get()
    // axios.post()
    // axios.request()
    // ....

    // axios发送多个请求
    axios.all([
        axios.get("http://httpbin.org/get"),
        axios.post("http://httpbin.org/post")
    ]).then(res => {
        console.log("res:", res);
    })
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script>
    // 设置axios的baseURL,如果设置了,后面的请求会自动添加baseURL
    let baseURL = "http://httpbin.org"
    axios.defaults.baseURL = baseURL;
    axios.defaults.timeout = 3000; // 设置超时时间  如果超时了  自动取消请求
    axios.defaults.headers = { // 给请求添加请求头
        a: 1,
        b: 2
    }

    axios.get("/get").then(res => {
        console.log("res:", res);
    })
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 7. axios的创建实例

为什么要创建axios的实例呢

  • 当我们从axios模块中导入对象时, 使用的实例是默认的实例
  • 当给该实例设置一些默认配置时, 这些配置就被固定下来了
  • 但是后续开发中, 某些配置可能会不太一样
  • 比如某些请求需要使用特定的baseURL或者timeout等
  • 这个时候, 我们就可以创建新的实例, 并且传入属于该实例的配置信息

面试题

  • 需求: 项目中有部分接口需要的配置与另一部分接口需要的配置不太一样, 如何处理
  • 解决: 创建2个新axios, 每个都有自己特有的配置, 分别应用到不同要求的接口请求中

注意点

  • 根据指定配置创建一个新的axios, 也就就每个新axios都有自己的配置
  • 新axios只是没有取消请求和批量发请求的方法, 其它所有语法都是一致的
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>

<script>
    // axios.get

    // 创建自己的实例  {}叫配置对象,这里面,就可以书写自己的配置
    let axios1 = axios.create({
        baseURL: "http://127.0.0.1:3000",
        timeout: 3000,
        headers: {}
    })

    axios1.get("/news", {
        params: {
            a: 1
        }
    }).then(res => {
        console.log("res:", res);
    })

    let axios2 = axios.create({
        baseURL: "http://127.0.0.1:5000",
        timeout: 5000,
        headers: {}
    })

    axios1.get("/students", {
        params: {
            a: 1
        }
    }).then(res => {
        console.log("res:", res);
    })
</script>
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

# 8. 请求和响应拦截器

axios的也可以设置拦截器:拦截每次请求和响应

  • axios.interceptors.request.use(请求成功拦截, 请求失败拦截)
  • axios.interceptors.response.use(响应成功拦截, 响应失败拦截)

前端代码

<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/nprogress/0.2.0/nprogress.min.js"></script>
<link href="https://cdn.bootcdn.net/ajax/libs/nprogress/0.2.0/nprogress.min.css" rel="stylesheet">

<button>发送请求</button>

<script>
    let btn = document.querySelectorAll("button")[0];

    // 给默认实例添加请求拦截器
    // 这样写,相当于,这个拦截器什么也没有做
    // axios.interceptors.request.use(config => {
    //     return config;
    // })

    // 所谓的做一些事,就是做一些配置
    axios.interceptors.request.use(config => {

        config.headers.Authorization = `fsadfsadf6789f87s6adf68asodfihasjkdfasdf7678fasd67f8asdfas`;

        NProgress.start(); // 显示进度条

        return config;
    }, err => {
        // 失败的回调,直接响应失败的promsie
        return Promise.reject(err)
    })

    axios.interceptors.response.use(response => {

        NProgress.done(); // 关闭进度条

        return response.data; // 仅仅把data过滤出来
    }, err => {
        // 失败的回调,直接响应失败的promsie
        return Promise.reject(err)
    })

    btn.onclick = function() {
        axios.get("http://httpbin.org/delay/5").then(res => {
            console.log(res);
        })
    }
</script>
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

面试题:拦截器函数/ajax请求/请求的回调函数的调用顺序

  • 说明: 调用axios()并不是立即发送ajax请求, 而是需要经历一个较长的流程
  • 流程: 请求拦截器2 => 请求拦截器1 => 发ajax请求 => 响应拦截器1 => 响应拦截器2 => 请求的回调
  • 注意: 此流程是通过promise串连起来的, 请求拦截器传递的是config, 响应拦截器传递的是response

# 9. 取消请求

基本流程

  • 配置cancelToken对象
  • 缓存用于取消请求的cancel函数
  • 在后面特定时机调用cancel函数取消请求
  • 在错误回调中判断如果error是cancel, 做相应处理

实现功能:点击按钮, 取消某个正在请求中的请求

  • 在请求一个接口前, 取消前面一个未完成的请求
<!-- axios源码   JS高级 -->

<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>

<button>发送请求</button>
<button>取消请求</button>

<script>
    let btns = document.querySelectorAll("button");
    let cancel;
    // 在请求一个接口前,取消前面没有完成的请求
    btns[0].onclick = async function() {
        let result = await axios("http://httpbin.org/delay/5", {
            cancelToken: new axios.CancelToken(c => {
                cancel = c;
            })
        });

        console.log("result:", result);
    }
    btns[1].onclick = function() {
        cancel(); // 手动取消上面的请求
    }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 10. Axios二次封装

功能点

  1. 统一进行请求配置: 基础路径/超时时间等
  2. 请求过程中loading提示
  3. 请求可能需要携带token数据
  4. 请求成功的value不再是response, 而是response.data
  5. 请求失败/出错统一进行处理, 每个请求可以不用单独处理
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>
    <script src="../node_modules/nprogress/nprogress.js"></script>
    <link rel="stylesheet" href="../node_modules/nprogress/nprogress.css">

    <!-- <script src="https://cdn.bootcdn.net/ajax/libs/nprogress/0.2.0/nprogress.min.js"></script>
    <link href="https://cdn.bootcdn.net/ajax/libs/nprogress/0.2.0/nprogress.min.css" rel="stylesheet"> -->
</head>

<body>

    <script>
        // 一般会自己创建一个axios实例    instance是实例的意思
        let instance = axios.create({
            // 在这里,可以做很多配置
            // baseURL是配置基础路径
            baseURL: "http://127.0.0.1:3000",
            timeout: 8000
        })

        // 目前在请求拦截器中配置了:
        //    1)开启Nprogress进度条
        //    2)在请求头中添加token
        instance.interceptors.request.use(config => {
            let token = localStorage.getItem("token") || "fasdfasdfsadf";
            config.headers["token"] = token;

            NProgress.start();

            return config;
        }, err => {
            return Promise.reject(err)
        })

        // 目前在响应拦截器中配置了:
        //    1)关闭Nprogress进度条
        //    2)过滤出data数据
        instance.interceptors.response.use(response => {

            NProgress.done();

            return response.data;
        }, err => {
            NProgress.done();
            return Promise.reject(err)
        })

        // 后面发请求,就可以使用自己的实例
        instance.get("http://httpbin.org/delay/5").then(res => {
            console.log(res);
        })
    </script>
</body>

</html>
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

# 11. JSONP原理

前端代码

<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>

<button id="btn">发送ajax请求</button>

<script>
    let btn = document.getElementById("btn");

    function jsonp(options) {

        let callBackName = "wangcai";

        window[callBackName] = function(data) {
            if (data != null) {
                options.success(data)
            } else {
                options.fail()
            }
        }

        let url = options.url + "?callBack=" + callBackName
        let scriptEle = document.createElement("script");
        scriptEle.src = url;
        document.body.append(scriptEle)
    }

    btn.onclick = function() {
        jsonp({
            url: "http://localhost:3000/",
            success: function(data) {
                console.log("data:", data);
            },
            fail: function(err) {
                console.log("数据请求失败了");
            }
        })
    }
</script>
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

后端代码

const Koa = require("koa");
const cors = require("koa2-cors");
const logger = require("koa-logger");
const Router = require("@koa/router");
const koaBody = require("koa-body");
const app = new Koa();
const router = new Router();

app.use(cors());
app.use(logger());
app.use(koaBody());

router.get("/", (ctx) => {
    let cb = ctx.query.callBack;
    // console.log(cb);

    // 后端返回函数调用字符串
    ctx.body = `${cb}(${JSON.stringify({ a: 1, b: 2 })})`
})

app.use(router.routes())
router.allowedMethods();

app.listen(3000, () => {
    console.log("running in http://127.0.0.1:3000");
});
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

# 12. 百度联想词案例

前端JS代码

  • 其它代码不在copy
;
(function() {
    // 接口地址:https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd=
    let searchInput = document.getElementsByClassName("J_searchInput")[0];
    let wdList = document.getElementsByClassName("J_wdList")[0];
    let listWrap = wdList.parentNode;

    searchInput.addEventListener("input", function() {
        let val = this.value.trim();
        if (val.length > 0) {
            getDatas(val, "setDatas");
        } else {
            wdList.innerHTML = "";
            listWrap.style.display = "none";
        }
    })

    // 调接口,获取数据
    function getDatas(value, callbackName) {
        let oScript = document.createElement("script");
        oScript.src = "https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd=" + value + "&cb=" + callbackName
        document.body.append(oScript)
    }

    window.setDatas = function(data) {
        // console.log("data:",data);
        // render是渲染的意思  List是列表的意思
        renderList(data)
    }

    // <li class="wd-item">
    //     <a href="https://www.baidu.com/s?wd={{wdLink}}" target="_blank" class="wd-lk">{{wd}}</a>
    // </li> 

    // 渲染数据到页面上
    function renderList(data) {
        var data = data.s;
        let len = data.length;
        let list = "";

        if (len) {
            data.forEach(item => {
                list += `
                    <li class="wd-item">
                        <a href="https://www.baidu.com/s?wd=${item}" target="_blank" class="wd-lk">${item}</a>
                    </li> 
                `
            })
            wdList.innerHTML = list;
            listWrap.style.display = "block";
        } else {
            wdList.innerHTML = "";
            listWrap.style.display = "none";
        }
    }
})()
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
Last Updated: 12/25/2022, 10:02:14 PM