从axios源码角度解决bug的过程记录

目录
  • 现象
  • 排查思路
    • 1. 引入 vConsole 在移动端调试
    • 2. 从大范围到小范围的 log
    • 3. axios 源码一览
    • 排查角度 - interceptor
    • 排查角度 - xhr
    • 4. 解决问题

现象

公司的一个 H5 站点在头条 App 里白屏,在手百、QQ 浏览器、Safari、Chrome 等都正常

排查思路

1. 引入 vConsole 在移动端调试

因为移动端没有 PC 里那样方便的调试工具可以清晰的查看 log 和 network 之类有用的信息,只能借助 vConsoleeruda 这类移动端调试工具,可以在移动端实现类似 PC 浏览器里的调试功能

2. 从大范围到小范围的 log

因为移动端无法 debugger,只能逐步的 log 去定位问题。我们采用的是 vue 技术栈,可以从 main.js 到路由组件的生命周期里去添加 log,通过这种方式定位到了在一个接口请求的方法之后的代码都不会请求,奇怪的是也没有抛出任何异常,并且在 vConsole 里的 network 下找到了该请求也是正常的响应,如下代码:

try {
    console.log('start fetch')
    const res = await FetchXXX();
    console.log(res)
} catch (e) {
    console.log(e)
}

只打印出了start fetch,请求的结果和捕获的异常里都没有打印出东西,至此又缩小了排查的范围,一定是请求的响应处理部分出现问题了。

因为我们的请求接口方法是基于 axios 统一封装的,本以为是封装的哪个环节有不兼容头条的代码,通过不断的 log,发现 request interceptor 都执行了,但是 response interctpor 一个都没执行,好家伙,看着不像我们封装的问题,应该是 axios 内部处理的问题。

3. axios 源码一览

通过上面的一顿操作,基本可以确认是 axios 内部处理响应数据时可能有部分兼容性问题,去 github 上去找 issue 也没找到相关的问题。 没办法只能去看 axios 的源码了,我们前面定位到是请求发送没问题,在响应的时候应该是遇到了啥异常,并且还没有抛出。

项目里 axios 用的版本是 0.24.0,我直接在 node_modules 里看 axios 的源码,因为 npm 下载的 axios 包没有用 webpack 或者 rollup 之类的编译过,所以在 node_modules 里看和看源码无异,并且更可靠(因为项目里是直接引用的这个代码)。打开 axios 源码目录

axios 的源码不算复杂,目录光看命名基本也能猜到是干啥的,几个主要目录如下:

adapters:针对不同的宿主环境使用不同的请求 api,目前只有浏览器端和 nodejs 端,adapters 目录下的 xhr.jshttp.js 分别对这两种环境进行了实现。

cancel:取消请求的相关源码

coreaxios 的核心源码

helpers:工具方法

排查角度 - interceptor

之前定位到所有的 response interceptor 都没执行,根据这个现象我先找到 interceptor 相关的代码,在 core/Axios.js 里找到以下代码:

// 请求拦截器列表
var requestInterceptorChain = [];
var synchronousRequestInterceptors = true;
// 遍历所有的请求拦截器并放入到列表中
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
    if (typeof interceptor.runWhen === 'function' && interceptor.runWhen(config) === false) {
        return;
    }
    synchronousRequestInterceptors = synchronousRequestInterceptors && interceptor.synchronous;
    // 请求拦截器后进先出(栈)
    requestInterceptorChain.unshift(interceptor.fulfilled, interceptor.rejected);
});
// 响应拦截器列表
var responseInterceptorChain = [];
// 遍历所有的响应拦截器并放入到列表中
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
    // 请求拦截器先进先出(队列)
    responseInterceptorChain.push(interceptor.fulfilled, interceptor.rejected);
});
var promise;
if (!synchronousRequestInterceptors) {
    // axios执行队列,包含所有的请求拦截器、请求方法、响应拦截器,并按顺序排列
    // 这里的dispatchRequest就是实际的请求方法
    var chain = [dispatchRequest, undefined];
    // 将所有的请求拦截器放请求的前面
    Array.prototype.unshift.apply(chain, requestInterceptorChain);
    // 将所有的响应拦截器放请求的后面
    chain = chain.concat(responseInterceptorChain);
    promise = Promise.resolve(config);
    // 依次执行整个执行队列,直至队列为空
    while (chain.length) {
        promise = promise.then(chain.shift(), chain.shift());
    }
    return promise;
}

以上代码就是 axiosinterceptor 核心的处理,一开始怀疑是不是所有的响应拦截器没有被加入到执行队列中,log 后发现没有问题,所有响应拦截器都在队列中。

排查角度 - xhr

上面排除了 interceptor 的问题,我又怀疑 axios 封装的 xhr 在响应的时候有啥兼容性问题,于是查看 adapters/xhr.js,并找到处理响应相关的代码:

// 省略了许多不必要的代码
var request = new XMLHttpRequest();
function onloadend() {
    console.log('onloadend')
    // ...
}
if ('onloadend' in request) {
    console.log('use onloadend')
    request.onloadend = onloadend;
} else {
    console.log('use onreadystatechange')
    request.onreadystatechange = function handleLoad() {
        if (!request || request.readyState !== 4) {
            return;
        }
        setTimeout(onloadend);
    };
}

axios 处理响应的代码大致如上,如果浏览器 xhr 对象包含 onloadend 事件就监听 onloadend 事件,否则监听 onreadystatechange 来实现请求响应的回调,通过 log 我发现打印了use onreadystatechange,但是没打印onloadend

至此终于找到问题所在,头条 iOS 该版本的 xhr 对象虽然声明了 onloadend 事件但是请求结束后并未回调该事件!

4. 解决问题

因为我们其他站点在头条上是可以正常访问的,我看了那些站点的代码,发现他们用的 axios 版本是 0.18.1,看下 0.18.1 版本的 adapters/xhr.js 文件对于响应的处理:

他是直接使用的 onreadystatechange 方法来监听的,所以没有问题。

至此整个排查结束,最后通过降级 axios 版本解决该问题。

以上就是从axios源码角度解决bug的过程记录的详细内容,更多关于axios源码角度bug解决的资料请关注我们其它相关文章!

(0)

相关推荐

  • Typescript 封装 Axios拦截器方法实例

    目录 引言 创建 class axios.create([config]) 封装 request(config)通用方法 封装-拦截器(单个实例独享) 扩展 Http 自定义拦截器 封装-拦截器(所有实例共享) 封装-拦截器(单个请求独享) 装修 Http class 返回经过 request 返回数据结构(DTO) 拦截器执行顺序 操作场景控制 引言 对 axios 二次封装,更加的可配置化.扩展性更加强大灵活 通过 class 类实现,class 具备更强封装性(封装.继承.多态),通过实例

  • vue3 axios 实现自动化api配置详解

    目录 概述 示例 约定 请求 URL 的约定 请求传参的约定 分页列表,请求参数约定 分页列表 响应示例 响应码 code 的约定 请求跨域问题解决方案 全局配置 配置说明coder/config.js 模型配置 1.实现对一个实体进行增.删.改.查.导出.唯一性校验 2.只需要增.删.改.查中得某些操作,可以指定生成你需要的方法 3.自定义方法配置 4.指定请求接口地址前缀 概述 在实践中,我们发现上述的代码重复率非常高,新增和修改都费力,并且是没技术含量的体力活. 但又必须要这样做,不适合以

  • 封装 axios+promise通用请求函数操作

    我就废话不多说了,大家还是直接看代码吧~ import axios from "axios"; import baseUrl from "../../setBaseUrl"; axios.defaults.baseURL = baseUrl; import { Loading, Message } from "element-ui"; const loadingOptions = { lock: true, text: "拼命加载中&q

  • axios库的核心代码解析及总结

    目录 一.关键步骤 1.创建axios对象 2.请求 二.Axios类 1.基础属性 2.辅助方法 3.request方法 三.adpter适配器 1.xhradpter 2.httpadpter 一.关键步骤 1.创建axios对象 axios库导出的对象是一个已经被创建好的axios对象,它本质上是一个方法,可以直接接收一个config配置参数进行请求.在库的入口处,即可看到如下代码: function createInstance(defaultConfig) { // 传入默认配置生成a

  • axios接口管理优化操作详解

    目录 强化功能 接口文件写法简化 任务调度.Loading调度 提示信息自由化 总结 强化功能 本文针对中大型的后台项目的接口模块优化,在不影响项目正常运行的前提下,增量更新. 接口文件写法简化(接口模块半自动化生成) 任务调度.Loading调度(接口层面的防抖兜底,多个接口共用一个loading,防止闪烁) 接口提示自由化(提示消息可由前端控制,也可以由后端控制) 接口文件写法简化 对于一些中后台模块的接口,基本上都是增删改查以及审核流的一些功能(其他特殊接口暂且不谈).如果后端接口足够规范

  • 前端axios取消请求总结详解

    目录 应用场景 如何取消请求 项目中用法示例 批量取消请求 切换路由时,取消请求 取消请求的实现原理 应用场景 取消请求在前端有时候会用到,以下是两个工作中可能会用到的场景 tab切换时刷新某个列表数据,如果他们共用一个变量存储数据列表,当请求有延时,可能会导致两个tab数据错乱: 导出文件或下载文件时,中途取消 . 如何取消请求 取消http请求,axios文档里提供了两种用法: 第一种:使用 CancelToken const { CancelToken, isCanCel } = axio

  • 浅谈vue使用axios的回调函数中this不指向vue实例,为undefined

    今天在vue-cli脚手架搭建的项目中使用axios时,遇到无法解析this.$route的报错信息,最后发现是作用域的问题. 1.解决方法:使用 => 原代码: axios.get('/user', { params: { ID: 12345 } }) .then(function (response) { console.log(response); }) .catch(function (error) { console.log(error); }); 修改为: axios.get('/u

  • 从axios源码角度解决bug的过程记录

    目录 现象 排查思路 1. 引入 vConsole 在移动端调试 2. 从大范围到小范围的 log 3. axios 源码一览 排查角度 - interceptor 排查角度 - xhr 4. 解决问题 现象 公司的一个 H5 站点在头条 App 里白屏,在手百.QQ 浏览器.Safari.Chrome 等都正常 排查思路 1. 引入 vConsole 在移动端调试 因为移动端没有 PC 里那样方便的调试工具可以清晰的查看 log 和 network 之类有用的信息,只能借助 vConsole.

  • Java从JDK源码角度对Object进行实例分析

    Object是所有类的父类,也就是说java中所有的类都是直接或者间接继承自Object类.比如你随便创建一个classA,虽然没有明说,但默认是extendsObject的. 后面的三个点"..."表示可以接受若干不确定数量的参数.老的写法是Objectargs[]这样,但新版本的java中推荐使用...来表示.例如 publicvoidgetSomething(String...strings)(){} object是java中所有类的父类,也就是说所有的类,不管是自己创建的类还是

  • Java源码角度分析HashMap用法

    -HashMap- 优点:超级快速的查询速度,时间复杂度可以达到O(1)的数据结构非HashMap莫属.动态的可变长存储数据(相对于数组而言). 缺点:需要额外计算一次hash值,如果处理不当会占用额外的空间. -HashMap如何使用- 平时我们使用hashmap如下 Map<Integer,String> maps=new HashMap<Integer,String>(); maps.put(1, "a"); maps.put(2, "b&quo

  • 从源码角度来回答keep-alive组件的缓存原理

    今天开门见山地聊一下面试中被问到的一个问题:keep-alive组件的缓存原理. 官方API介绍和用法 <keep-alive> 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们. 和 <transition> 相似,<keep-alive> 是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在组件的父组件链中. 当组件在 <keep-alive> 内被切换,它的 activated 和 deactivated 这两个生命周期钩子函数将会被对

  • 从源码角度分析Android的消息机制

    前言 说到Android的消息机制,那么主要的就是指的Handler的运行机制.其中包括MessageQueue以及Looper的工作过程. 在开始正文之前,先抛出两个问题: 为什么更新UI的操作要在主线程中进行? Android中为什么主线程不会因为Looper.loop()里的死循环卡死? UI线程的判断是在ViewRootImpl中的checkThread方法中完成的. 对于第一个问题,这里给一个简单的回答: 如果可以在子线程中修改UI,多线程的并发访问可能会导致UI控件的不可预期性,采用

  • vue3 keepalive源码解析解决线上问题

    目录 引言 1.keepalive功能 2.keepalive使用场景 3.在项目中的使用过程 4.vue3 keepalive源码调试 5.vue3 keealive源码粗浅分析 6.总结 引言 1.通过本文可以了解到vue3 keepalive功能 2.通过本文可以了解到vue3 keepalive使用场景 3.通过本文可以学习到vue3 keepalive真实的使用过程 4.通过本文可以学习vue3 keepalive源码调试 5.通过本文可以学习到vue3 keepalive源码的精简分

  • Spring源码完美导入IDEA的过程

    小伙伴私信我说想要研究下Spring的源码,想让我出一期教程来实现IDEA导入Spring源码,今天它来了~ 版本 :IDEA 2020.2.3 ;Spring 5.0.x ;gradle 4.4.1 ; 先从github上面把 spring 源码下载下来并解压: 切记:解压完成后不要直接用IDEA 打开,因为Spring 的源码是用 gradle 构建的.如果已经用IDEA 打开了请删除后重新解压. 我们找到文件夹 spring-framework-5.0.x\gradle\wrapper 下

  • Vue 源码分析之 Observer实现过程

    导语: 本文是对 Vue 官方文档深入响应式原理(https://cn.vuejs.org/v2/guide/reactivity.html)的理解,并通过源码还原实现过程. 响应式原理可分为两步,依赖收集的过程与触发-重新渲染的过程.依赖收集的过程,有三个很重要的类,分别是 Watcher.Dep.Observer.本文主要解读 Observer . 这篇文章讲解上篇文章没有覆盖到的 Observer 部分的内容,还是先看官网这张图: Observer 最主要的作用就是实现了上图中touch

  • Hadoop源码分析二安装配置过程详解

    目录 1. 创建用户 2. 安装jdk 3. 修改hosts 4. 配置ssh免密登录 5. 安装zookeeper 解压: 修改配置文件 修改内容如下: 配置环境变量 启动 6. 安装hadoop 对于三台节点的配置安排如下: 解压: 修改配置文件: 修改core-site.xml 配置hdfs-site.xml 配置mapred-site.xml 配置yarn-site.xml 配置slaves 7. 初始化 在初始化前需要将所有机器都配置好hadoop (1) 启动zookeeper (2

  • Android 2.3 拨号上网流程从源码角度进行分析

    通常,如果我们想使用SIM卡拨号上网功能,我们要在设置中进行简单的配置,步骤如下: 设置 ->无线和网络 ->移动网络 ->(已启用数据/数据漫游/接入点名称/仅使用2G网络/网络运营商) 我们必须选中其中的"已启用数据"选项,然后配置接入点名称后就可以上网了,当然有的设置中已经根据你的SIM卡类型默认设置了接入点,这时候你只选择"已启用数据"项后就可以完成上网功能设置. 这些设置步骤究竟做了哪些事情呢?我们现在就从源码的角度进行分析. 1. 首先

随机推荐