关于我对axios的源码理解

本文详细介绍了一个简易版Axios请求库的实现过程,包括构造函数定义、原型方法创建、拦截器管理及取消请求功能等核心组件的设计与实现。

1.创建构造函数

原始构造函数用于存储默认属性与拦截器操作

拦截器管理器构造函数InterceptorManager 主要是为了存储拦截器的失败与成功操作函数

function Axios(config) {
    // 数据初始化
    this.defaults = config // 默认属性
    this.intercepters = { // 拦截器操作
        request: new InterceptorManager(), // 用于请求前拦截
        response: new InterceptorManager(), // 用于响应结果拦截
    }
}

2.创建Axios构造函数的原型方法(get,post,request)

重点是request函数,主要是用promise来进行链式操作

创造promise

chain存储axios请求函数dispatchRequest,undefined(失败占位符)

请求拦截在头部插入

响应拦截在尾部插入

真实axios请求在(请求拦截)与(响应拦截)之间

先执行请求拦截再执行真是axios请求,最后执行响应拦截

while 保证全部执行

// 创建原型方法

Axios.prototype.get = function (config) {
    return this.request({ ...config, method: 'GET', })
}

Axios.prototype.post = function (config) {
    return this.request({ ...config, method: 'POST' })
}

Axios.prototype.request = function (config) {
    console.log('发送 AJAX 请求 请求的类型为 ' + config.method);

    // 发送请求
    // 创建promise对象
    let promise = Promise.resolve(config);
    //发送请求
    let chain = [dispatchRequest, undefined];//undefined进行占位
    // 请求拦截
    this.intercepters.request.handlers.forEach(item => {
         //头部插入,后写入的先执行
        chain.unshift(item.fulfilled, item.injected)
    })
    // debugger
    //响应拦截器
    this.intercepters.response.handlers.forEach(item => {
        //尾部插入,后写入的后执行
        chain.push(item.fulfilled, item.rejected);
    });
    //遍历
    while (chain.length > 0) {
        //保证全部执行,成功函数,失败函数
        promise = promise.then(chain.shift(), chain.shift());
    }
    return promise;
}

3.创建dispatchRequest 函数

function dispatchRequest(config) {
    return xhrRequest(config).then(response => {
        //响应的结果进行转换处理
        //....
        return response;
    }, error => {
        throw error;
    });
}

4. xhrRequest发送请求

promise进行xhr请求

当存在cancelToken的时候进行取消操作

promise.then的执行时机是在cancel方法改变promise状态才执行

function xhrRequest(config) {
    return new Promise((reslove, inject) => {
        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({
                        config: config,
                        data: xhr.response,
                        Headers: xhr.getAllResponseHeaders(),
                        request: xhr,
                        status: xhr.status,
                        statusText: xhr.statusText
                    })

                } else {
                    inject(new Error('请求失败 失败的状态码为' + xhr.status))
                }
            }
        }
        //关于取消请求的处理
        if (config.cancelToken) {
            //对 cancelToken 对象身上的 promise 对象指定成功的回调
            config.cancelToken.promise.then(value => {
                xhr.abort();
                console.log('请求已经被取消')
                //将整体结果设置为失败
                inject(new Error('请求已经被取消'))
            });
        }
    })
}

6.拦截器管理器构造函数

function InterceptorManager() {
    this.handlers = [];
}
InterceptorManager.prototype.use = function (fulfilled, rejected) {
    this.handlers.push({
        fulfilled,
        rejected
    })
}

7.创建axios声明函数,生成axios,并将Axios的原型方法跟默认属性添加到axios上

function createInstance(config) {

    // 实例化一个对象 , bind绑定this而不执行
    let context = new Axios(config)
    let instance = Axios.prototype.request.bind(context);
    // console.log(instance)
    // 将context的原型方法添加到instance上
    Object.keys(Axios.prototype).forEach(key => {
        instance[key] = Axios.prototype[key].bind(context)

    })
    // 将context中的default 与intercepters添加到instance上
    Object.keys(context).forEach(key => {
        instance[key] = context[key]
    })

    // console.dir(instance)
    return instance;
}

8.绑定拦截器事件

axios.intercepters.request.use(function one(config) {
    console.log('请求拦截器 成功 - 1号');
    return config;
}, function one(error) {
    console.log('请求拦截器 失败 - 1号');
    return Promise.reject(error);
});

axios.intercepters.request.use(function two(config) {
    console.log('请求拦截器 成功 - 2号');
    return config;
}, function two(error) {
    console.log('请求拦截器 失败 - 2号');
    return Promise.reject(error);
});

// 设置响应拦截器
axios.intercepters.response.use(function (response) {
    console.log('响应拦截器 成功 1号');
    return response;
}, function (error) {
    console.log('响应拦截器 失败 1号')
    return Promise.reject(error);
});

axios.intercepters.response.use(function (response) {
    console.log('响应拦截器 成功 2号')
    return response;
}, function (error) {
    console.log('响应拦截器 失败 2号')
    return Promise.reject(error);
});

9. CancelToken 构造函数用来取消请求

创造promise,先不执行resolve成功函数,而是将resolve存储起来,将function(){resolvePromise();}作为参数传递给cancel

function CancelToken(executor){
    //声明一个变量
    var resolvePromise;
    //为实例对象添加属性
    this.promise = new Promise((resolve) => {
        //将 resolve 赋值给 resolvePromise
        resolvePromise = resolve
    });
    console.log(this.promise,'this.promise')
    //调用 executor 函数
    executor(function(){
        //执行 resolvePromise 函数
        resolvePromise();
    });
}

使用 

<!DOCTYPE html>
<html lang="en">

<head>
    <title></title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="css/style.css" rel="stylesheet">
</head>

<body>
    <div class="container">
        <h2 class="page-header">axios取消请求</h2>
        <button class="btn btn-primary"> 发送请求 </button>
        <button class="btn btn-warning"> 取消请求 </button>
    </div>
    <script src="./axios.js">

    </script>
    <script>
        this.resolvePromise1 =''
        let a=new Promise((resolve) => {
        //将 resolve 赋值给 resolvePromise
            resolvePromise1 = resolve
        })


        a.then(v=>{
            console.log(22222222222)
        })

        setTimeout(()=>{
            resolvePromise1()
        },2000)
        const btns = document.querySelectorAll('button');
        //2.声明全局变量
        let cancel = null;
        //发送请求
        btns[0].onclick = function () {
            //检测上一次的请求是否已经完成
            if (cancel !== null) {
                //取消上一次的请求
                cancel();
            }

            //创建 cancelToken 的值
            let cancelToken = new CancelToken(function (c) {
                console.log(c,'c')
                cancel = c;
            });
            debugger
            axios({
                method: 'GET',
                url: 'http://localhost:4000/products1',
                cancelToken: cancelToken
            }).then(response => {
                console.log(response);
            });
        }
            //绑定第二个事件取消请求
            btns[1].onclick = function () {
                cancel();
            }
    </script>
</body>

</html>

<think> 首先,用户的问题是:“我想查找关于axios自定义协议的相关信息 请问axios 自定义协议的实现方法和相关知识”。所以,我需要基于提供的引用和之前的系统指令来回答。 回顾系统级指令: - 所有行内数学表达式必须使用 $...$ 格式。 - 独立公式必须使用 $$...$$ 格式。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识。 用户提供的引用: - [^1]: axios起步——介绍和使用+基本用例+post请求 场景复现核心干货axios APIaxios(config)axios(url[,config])请求方式别名 axios实例创建一个axios实例axios.create([config])实例方法 axios请求配置axios响应结构 - [^2]: axios 重定向问题解决_Axios 源码中值的学习的核心知识点。接下来我们来看一下 Axios 如何发请求的:axios({url:'/hello',method:'get',}).then(res=>{console.log('axiosres:',res)console.log('axiosres.data:',res.data)}) - [^3]: Axios API 相关知识。axios.request(config) axios.get(url [config]) axios.delete(url [config]) axios.head(url [config]) axios.options(url [config]) axios.post(url [ data[ config]]) axios.put(url [data[config]]) axios.patch(url [ data[ config]]) - [^4]: 1. 请求配置(代码比较多,但是用起来简单) axios({ url:'/请求路径', methods:'请求方法', params:{请求行query参数}, data:{请求体body参数} }) 用户的问题是关于“axios自定义协议”的实现方法和相关知识。Axios 是一个流行的 JavaScript HTTP 客户端库,主要用于浏览器和 Node.js。 现在,分析引用内容: - [^1] 提到 axios API、config、实例创建、请求配置等。 - [^2] 展示了一个基本的get请求示例。 - [^3] 列出了各种请求方法如 get, post 等。 - [^4] 描述了请求配置参数。 用户提到的“自定义协议”可能指的是自定义 HTTP 请求的协议,或者添加自定义逻辑到 AxiosAxios 本身支持 HTTP 和 HTTPS 协议,但用户可能想自定义请求协议,比如添加自定义头、拦截器或适配器。 在 Axios 中,自定义协议可能涉及: 1. **自定义请求头**:使用 headers 在 config 中设置。 2. **拦截器**:用于在请求发送前或响应接收后添加逻辑。 3. **适配器**:Axios 允许自定义适配器来修改底层请求机制。 4. **自定义实例**:创建实例时设置默认配置。 引用中没有直接提到“自定义协议”,但有相关配置: - [^4] 提到 config 中的 url, methods, params, data。 - [^1] 提到 axios.create 和实例方法。 - [^3] 列出 API 方法。 我的回答应该: - 解释如何实现自定义协议,可能通过配置、拦截器或适配器。 - 使用中文回答。 - 结构清晰,逐步解决问题。 - 保证真实可靠:基于我对 Axios 的知识(Axios 不支持直接自定义协议如 HTTP/2 或 WebSockets,但可以通过适配器扩展)。 - 如果有引用,在末尾添加引用标识。 - 生成相关问题。 步骤: 1. **理解需求**:用户问“axios自定义协议”,我认为是自定义请求的协议相关设置,如添加自定义认证、修改 headers 等。 2. **核心知识点**: - Axios 配置:config 对象可以包含 url, method, headers, data 等。 - 自定义适配器:Axios 允许自定义适配器来处理请求,这可以用于支持非标准协议或修改底层传输。 - 拦截器:用于全局修改请求或响应。 - 创建实例:设置默认配置。 3. **基于引用**:引用 [^4] 提到 config 参数,包括 url, methods, params, data。引用 [^1] 提到实例创建。 4. **回答结构**: - 介绍 Axios 自定义协议的含义。 - 解释实现方法:通过配置、拦截器、适配器。 - 提供代码示例(基于引用)。 - 总结。 - 生成相关问题。 5. **引用标识**:如果在回答中引用特定部分,在末尾添加如 [^1]。 6. **数学表达式**:用户问题不涉及数学,所以可能不需要,但如果有地方适用,确保格式正确。 7. **生成相关问题**:在最后添加几个相关问题。 实现方法: - 自定义协议可能不是标准术语;在 Axios 中,这通常是通过自定义适配器来扩展支持其他协议。 - 例如,Axios 默认使用 XMLHttpRequest 或 http 模块,但可以自定义适配器使用 fetch 或其他库。 - 标准做法:使用 config 的 adapter 属性。 代码示例基于引用: - 从 [^4],config 包括 url, method 等。 - 添加自定义适配器示例。 最终回答大纲: 1. 解释什么是自定义协议在 Axios 上下文。 2. 实现方法: - 使用 config 设置 headers 或其他。 - 创建自定义适配器。 - 使用拦截器。 3. 代码示例。 4. 相关问题。 </think> ### Axios 自定义协议的实现方法和相关知识 在 Axios 中,“自定义协议”通常指的是扩展或修改 HTTP 请求的底层协议行为,例如添加自定义请求头和认证机制、集成非标准通信协议(如 WebSocket 或 gRPC),或自定义请求适配器来支持特定传输层。Axios 本身是一个基于 Promise 的 HTTP 客户端,默认支持 HTTP/HTTPS 协议,但通过其灵活的配置系统(如 `config` 对象、拦截器和自定义适配器),可以轻松实现协议的定制化。以下基于提供的引用和 Axios 官方文档[^1][^3][^4],逐步解释实现方法和相关知识。 #### 1. **自定义协议的核心概念** - **什么是自定义协议?** 在 Axios 中,这不是一个内置功能,而是指通过配置覆盖请求的默认行为,例如: - 修改请求头(headers)添加自定义认证协议(如 JWT 或 OAuth)。 - 使用适配器(adapter)替换底层传输机制(如改用 `fetch` 或支持 WebSocket)。 - 通过拦截器(interceptors)全局注入协议相关逻辑(如重定向处理或错误处理)。 这种定制常用于处理特殊场景,如跨域安全协议、自定义序列化或集成第三方协议库[^1][^4]。 - **为什么需要自定义协议?** - 解决特定协议兼容性问题(例如老旧系统使用非 HTTP 协议)。 - 添加企业级安全策略(如自定义加密头)。 - 优化性能(如复用连接或压缩数据)。 Axios 的设计基于 `config` 对象,使其高度可扩展[^1][^3]。 #### 2. **实现自定义协议的方法** Axios 提供了多种方式实现协议定制,核心是通过 `config` 参数(引用中多次强调[^1][^3][^4])。以下是具体实现步骤: - **方法一:通过 `config` 对象直接自定义请求协议** 在请求配置中,使用 `headers`、`data` 或 `params` 添加协议相关参数。例如,添加自定义认证头或修改请求方法: ```javascript // 示例:自定义协议头和安全认证 axios({ url: '/api/data', // 请求路径 method: 'post', // 请求方法 headers: { 'X-Custom-Protocol': 'my-protocol-v1', // 自定义协议标识符 'Authorization': 'Bearer your-token' // 认证协议 }, data: { key: 'value' } // 请求体数据 }).then(response => { console.log(response.data); // 处理响应 }).catch(error => { console.error('协议错误:', error); }); ``` - **说明**:此方法适用于简单场景,如添加协议头。引用 [^4] 提到 `config` 包含 `url`, `methods`, `params`, `data` 等参数,可直接用于协议定制[^4]。 - **优点**:简单快捷,无需修改底层代码。 - **方法二:使用自定义适配器(adapter)替换底层协议** Axios 允许覆盖默认适配器(浏览器端用 XMLHttpRequest,Node.js 端用 http 模块),以实现完全自定义的协议逻辑。例如,集成 WebSocket 协议: ```javascript // 步骤1: 创建自定义适配器函数 const customAdapter = (config) => { // 实现自定义协议逻辑,如使用 WebSocket return new Promise((resolve, reject) => { const socket = new WebSocket(config.url); socket.onmessage = (event) => resolve({ data: event.data }); socket.onerror = (error) => reject(error); socket.send(JSON.stringify(config.data)); }); }; // 步骤2: 在请求中应用自定义适配器 axios({ url: 'ws://example.com', // WebSocket协议URL adapter: customAdapter, // 注入自定义适配器 data: { message: 'Hello' } }).then(res => { console.log('响应数据:', res.data); }); ``` - **说明**:适配器是 Axios 的核心扩展点,引用 [^1] 提到 `axios.create` 和实例方法可重用配置,适合封装协议适配器[^1]。 - **注意事项**: - 适配器需返回 Promise 对象。 - 适用于高级场景,如支持 HTTP/2 或 gRPC。 - **方法三:通过拦截器(interceptors)全局处理协议逻辑** 拦截器可以在请求发送前或响应接收后添加协议相关处理,例如自动添加协议头或处理重定向: ```javascript // 添加请求拦截器 axios.interceptors.request.use( (config) => { // 自定义协议逻辑:为所有请求添加协议版本头 config.headers['X-Protocol-Version'] = '1.0'; return config; }, (error) => Promise.reject(error) ); // 添加响应拦截器处理协议错误 axios.interceptors.response.use( (response) => response, (error) => { if (error.response && error.response.status === 301) { // 自定义重定向协议处理 return axios.get(error.response.headers.location); } return Promise.reject(error); } ); // 使用示例 axios.get('/resource').then(res => console.log(res.data)); ``` - **说明**:引用 [^2] 提到 Axios 如何发请求和处理响应,拦截器可扩展此流程来处理协议级问题(如重定向)[^2]。 - **优点**:统一管理协议规则,减少重复代码。 #### 3. **相关知识要点** - **Axios 配置基础**:所有自定义协议都依赖于 `config` 对象,其结构包括 `url`, `method`, `headers`, `data`, `params`, `adapter` 等(引用 [^3][^4] 详细列出)[^3][^4]。 - **协议定制限制**: - Axios 默认仅支持 HTTP/HTTPS,自定义适配器需自行实现其他协议(如 WebSocket)。 - 安全考虑:自定义协议可能引入漏洞,建议遵循安全最佳实践(如使用 HTTPS)。 - **性能影响**:复杂协议逻辑可能增加延迟,推荐使用 Axios 实例(`axios.create`)预配置优化(引用 [^1] 提到实例方法)[^1]。 - **常见场景**: - 企业 API 集成:添加自定义认证协议头。 - 跨平台应用:适配不同环境(浏览器 vs Node.js)。 - 兼容性处理:支持老旧系统协议。 #### 4. **总结** 在 Axios 中实现自定义协议,核心是通过 `config` 配置、自定义适配器或拦截器来扩展请求行为。简单修改使用 `headers` 或 `data`,高级定制依赖适配器覆盖底层传输。确保代码可维护性时,优先使用拦截器或创建 Axios 实例(参考引用中的 `axios.create`[^1])。如需处理非 HTTP 协议,需结合第三方库(如 `ws` for WebSocket)。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值