axios全面解析

axios中文文档|axios中文网 | axios

1. axios一些优点介绍

axios的特点以及使用_Luckyzhoufangbing的博客-CSDN博客_axios的优点

2. axios的使用

使用 npm:

$ npm install axios

使用 bower:

$ bower install axios

使用 cdn:

<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<!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://unpkg.com/axios/dist/axios.min.js"></script>
    <style>
        .btn {
            width: 200px;
            height: 100px;
            border: 1px salmon solid;
            background-color: cadetblue;
            color: aliceblue;
            cursor: pointer;
        }
    </style>
</head>

<body>
    <button class="btn">get</button>
    <button class="btn">post</button>
    <button class="btn">put</button>
    <button class="btn">delete</button>
    <script>
        axios.defaults.baseURL = "http://127.0.0.1:7000"
        axios.defaults.timeout = 3000
        const btns = document.getElementsByClassName("btn")
        btns[0].onclick = function () {
            axios({
                method: "get",
                url: `get`,
                params: {
                    name: "get"
                }
            }).then(res => {
                console.log(res);
            }).catch(err => {
                console.warn(err);
            })
        }
        btns[1].onclick = function () {
            // axios({
            //     method: "post",
            //     url: `http://127.0.0.1:7000/post`,
            //     data: {
            //         name: "post请求"
            //     }
            // }).then(res => {
            //     console.log(res);
            // }).catch(err => {
            //     console.warn(err);
            // })
            // 1,url  2, data 3, config
            axios.post(
                "/post",
                {
                    name: "post请求"
                })
        }
        btns[2].onclick = function () {
            axios({
                method: "put",
                url: `/put`,
                data: {
                    name: "put请求"
                }
            }).then(res => {
                console.log(res);
            }).catch(err => {
                console.warn(err);
            })
        }
        btns[3].onclick = function () {
            axios({
                method: "delete",
                url: `/delete`,
                data: {
                    name: "delete请求"
                }
            }).then(res => {
                console.log(res);
            }).catch(err => {
                console.warn(err);
            })
        }
        //也可以直接创建axios的对象
        const request = axios.create({
            //配置项
        })
        console.log(request);
    </script>
</body>

</html>

 3. 拦截器

<!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://unpkg.com/axios/dist/axios.min.js"></script>
    <style>
        .btn {
            width: 200px;
            height: 100px;
            border: 1px salmon solid;
            background-color: cadetblue;
            color: aliceblue;
            cursor: pointer;
        }
    </style>
</head>

<body>
    <button class="btn">get</button>
    <button class="btn">post</button>
    <button class="btn">put</button>
    <button class="btn">delete</button>
    <script>
        axios.defaults.baseURL = "http://127.0.0.1:7000"
        axios.defaults.timeout = 3000
        function bind() {
            const btns = document.getElementsByClassName("btn")
            btns[0].onclick = function () {
                axios({
                    method: "get",
                    url: `get`,
                    params: {
                        name: "get"
                    }
                }).then(res => {
                    console.log(res);
                }).catch(err => {
                    console.warn(err);
                })
            }
            btns[1].onclick = function () {
                axios.post(
                    "/post",
                    {
                        name: "post请求"
                    })
            }
            btns[2].onclick = function () {
                axios({
                    method: "put",
                    url: `/put`,
                    data: {
                        name: "put请求"
                    }
                }).then(res => {
                    console.log(res);
                }).catch(err => {
                    console.warn(err);
                })
            }
            btns[3].onclick = function () {
                axios({
                    method: "delete",
                    url: `/delete`,
                    data: {
                        name: "delete请求"
                    }
                }).then(res => {
                    console.log(res);
                }).catch(err => {
                    console.warn(err);
                })
            }
        }
        bind()
        //请求拦截器
        axios.interceptors.request.use(function (config) {
            //config就是该请求的配置
            //例如 config.params={a:500}
            console.log("请求拦截成功");
            return config
        }, function (err) {
            console.log("请求拦截失败");
            return Promise.reject(err)
        })
        //响应拦截器
        axios.interceptors.response.use(function (response) {
            console.log("响应拦截成功");
            //默认的响应结果
            return response
        }, function (err) {
            console.log("响应拦截失败");
            return Promise.reject(err)
        })
    </script>
</body>

</html>

4. 取消请求

        let cancel = null
            const btns = document.getElementsByClassName("btn")
            btns[0].onclick = function () {
                //发新的取消旧的
                if(cancel){
                    cancel()
                }
                axios({
                    method: "get",
                    url: `get`,
                    params: {
                        name: "get"
                    },
                    cancelToken: new axios.CancelToken(function (c) {
                        //cancel现在就是取消事件了
                        cancel = c
                    })
                }).then(res => {
                    console.log(res);
                    cancel = null
                }).catch(err => {
                    console.warn(err);
                })
            }

5. 源码解析

(1).模拟制造过程

axios实例对象之所以又可以当函数又可以当对象调用上面的属性方法(axios(),axios.get())是因为源码中instance函数不仅本身是函数,并且使用extend函数将Axios显示原型上的方法全部继承了过来。instance不过就是Axios.prototype.request.bind().其实和request的功能一模一样。

<!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>
</head>

<body>
    <script>
        function Axios(config) {
            this.defaults = config
            this.interceptors = {
                request: {},
                response: {}
            }
        }
        Axios.prototype.request = function (config) {
            console.log("发送请求" + config.method);
        }
        Axios.prototype.get = function (config) {
            return this.request({ method: "get" })
        }
        Axios.prototype.post = function (config) {
            return this.request({ method: "post" })
        }
        function createInstance(config) {
            //还不能当函数用
            let context = new Axios(config)
            //instance不能当对象用
            let instance = Axios.prototype.request.bind(context)
            //获取对象的方法
            Object.keys(Axios.prototype).forEach((key) => {
                instance[key] = Axios.prototype[key].bind(context)
            })
            //为他添加构造器属性
            Object.keys(context).forEach(key => {
                instance[key] = context[key]
            })
            return instance
        }
        let axios = createInstance({ method: "get" })
        axios.get()
        axios({method:"get"})
    </script>
</body>

</html>

(2). 发送请求

request调用dispatch,dispatch调用我们的http或者xhr(因为要适配node环境和浏览器环境)。保证返回的响应,返回到我们axios函数的promise回调。

代码实现如下:

<!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>
</head>

<body>
    <script>
        function Axios(config) {
            this.config = config
        }
        Axios.prototype.request = function (config) {
            //发送请求
            let promise = Promise.resolve(config)
            let chains = [dispatchRequest, undefined]
            let result = promise.then(chains[0], chains[1])
            return result
        }
        function dispatchRequest(config) {
            return xhrAdapter(config)
                .then(res => {
                    //转化响应结果
                    return res
                })
                .catch(err => {
                    throw Error("err")
                })
        }
        function xhrAdapter(config) {
            return new Promise((resolve, reject) => {
                //发送请求
                let xhr = new XMLHttpRequest()
                xhr.open(config.method.toUpperCase(), config.url)
                xhr.send()
                xhr.onreadystatechange = function () {
                    if (xhr.readyState == 4) {
                        if (xhr.status >= 200 && xhr.status < 300) {
                            resolve({
                                config,
                                data: xhr.response,
                                headers: xhr.getAllResponseHeaders(),
                                request: xhr,
                                status: xhr.status,
                                statusText: xhr.statusText
                            })
                        } else {
                            reject(new Error(xhr.status))
                        }
                    }
                }
            })
        }
        let axios = Axios.prototype.request.bind(null)
        axios({
            url: "http://127.0.0.1:7000/get",
            method: "get"
        })
    </script>
</body>

</html>

(3) 拦截器

首先是原理,在发送请求时有一个chains,他其实就是我们指向任务的一个链,当添加请求拦截器时,会通过unshift逆序添加到chains的头部,当添加响应拦截器时会顺序push到我们的chains尾部。循环遍历chains挨着执行。

代码模拟

<!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>
</head>

<body>
    <script>
        function Axios(config) {
            this.config = config
            this.interceptors = {
                request: new InterceptorManager(),
                response: new InterceptorManager()
            }
        }
        Axios.prototype.request = function (config) {
            let promise = Promise.resolve(config)
            let chains = [dispatchRequest, undefined]
            //处理请求拦截器
            this.interceptors.request.handlers.forEach(item => {
                chains.unshift(item.fulfilled, item.reject)
            })
            //处理响应拦截器
            this.interceptors.response.handlers.forEach(item => {
                chains.push(item.fulfilled, item.reject)
            })
            console.log(chains);
            while (chains.length > 0) {
                promise = promise.then(chains.shift(), chains.shift())
            }
            return promise
        }
        function dispatchRequest(config) {
            return new Promise((reslove, reject) => {
                reslove({
                    status: 200,
                    statusText: "ok"
                })
            })
        }
        function InterceptorManager() {
            this.handlers = []
        }
        InterceptorManager.prototype.use = function (fulfilled, reject) {
            this.handlers.push({
                fulfilled,
                reject
            })
        }
        let context = new Axios({})
        let axios = Axios.prototype.request.bind(context)
        Object.keys(context).forEach(key => {
            axios[key] = context[key]
        })
        //请求拦截器
        axios.interceptors.request.use(function (config) {
            //config就是该请求的配置
            //例如 config.params={a:500}
            console.log("请求拦截成功");
            return config
        }, function (err) {
            console.log("请求拦截失败");
            return Promise.reject(err)
        })
        //响应拦截器
        axios.interceptors.response.use(function (response) {
            console.log("响应拦截成功");
            //默认的响应结果
            return response
        }, function (err) {
            console.log("响应拦截失败");
            return Promise.reject(err)
        })
        axios().then(res => {
            console.log(res);
        }).catch(err => {
            console.log(err);
        })
    </script>
</body>

</html>

(4) 取消请求

这里可以说是把promise玩到了极致,这里有一种很有意思的开发思路,如果我们在运行过程中不确定是否会执行一段逻辑,可以将它设置为promise成功的回调,但是我们的这个promise的reslove可以赋值给外部变量,这样当外部变量函数执行时,promise状态改变,执行then中的成功回调,进而执行我们提前准备好的逻辑,如果这个外部函数永远不执行,那么该promise的状态就会一直保持是padding状态,该逻辑就永远不会执行。

<!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>
</head>

<body>
    <button>发送</button>
    <button>取消</button>
    <script>
        function Axios(config) {
            this.config = config
        }
        function CancelToken(executor) {
            let reslovePromise
            this.promise = new Promise((reslove) => {
                reslovePromise = reslove
            })
            executor(function () {
                reslovePromise()
            })
        }
        Axios.prototype.request = function (config) {
            return dispatchRequest(config)
        }
        function dispatchRequest(config) {
            return xhrAdapter(config)
        }
        function xhrAdapter(config) {
            return new Promise((reslove, reject) => {
                let xhr = new XMLHttpRequest()
                xhr.open(config.method, config.url)
                xhr.send() 
                xhr.onreadystatechange = function () {
                    if (xhr.readyState === 4) {
                        if (xhr.status >= 200 && xhr, status < 300) {
                            reslove(xhr.response)
                        } else {
                            reject(xhr.statusText)
                        }
                    }
                }
                if (config.cancelToken) {
                    config.cancelToken.promise.then(res => {
                        xhr.abort()
                    })
                }
            })
        }
        let context = new Axios({})
        let axios = Axios.prototype.request.bind(context)
        let btns = document.getElementsByTagName("button")
        let cancel = undefined
        btns[0].onclick = function () {
            axios({
                url: "http://127.0.0.1:7000/get",
                method: "GET",
                cancelToken: new CancelToken(function (c) {
                    cancel = c
                }),
            }).then(res => {
                console.log(res);
            }).catch(err => {
                console.log(err);
            })
        }
        btns[1].onclick = function () {
            cancel()
        }
    </script>
</body>

</html>

猜你喜欢

转载自blog.csdn.net/weixin_47964837/article/details/125488464