2024年源码拾遗系列之Axios,2024年最新前端阿里面试题

文末

技术是没有终点的,也是学不完的,最重要的是活着、不秃。

零基础入门的时候看书还是看视频,我觉得成年人,何必做选择题呢,两个都要。喜欢看书就看书,喜欢看视频就看视频。

最重要的是在自学的过程中,一定不要眼高手低,要实战,把学到的技术投入到项目当中,解决问题,之后进一步锤炼自己的技术。

自学最怕的就是缺乏自驱力,一定要自律,杜绝“三天打鱼两天晒网”,到最后白忙活一场。

高度自律的同时,要保持耐心,不抛弃不放弃,切勿自怨自艾,每天给自己一点点鼓励,学习的劲头就会很足,不容易犯困。

技术学到手后,找工作的时候一定要好好准备一份简历,不要无头苍蝇一样去海投简历,容易“竹篮打水一场空”。好好的准备一下简历,毕竟是找工作的敲门砖。

拿到面试邀请后,在面试的过程中一定要大大方方,尽力把自己学到的知识舒适地表达出来,不要因为是自学就不够自信,给面试官一个好的印象,面试成功的几率就会大很多,加油吧,骚年!

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

// Add xsrf header

// This is only done if running in a standard browser environment.

// Specifically not if we’re in a web worker, or react-native.

if (utils.isStandardBrowserEnv()) {

// Add xsrf header

var xsrfValue = (config.withCredentials || isURLSameOrigin(fullPath)) && config.xsrfCookieName ?

cookies.read(config.xsrfCookieName) :

undefined;

if (xsrfValue) {

requestHeaders[config.xsrfHeaderName] = xsrfValue;

}

}

Interceptor

拦截器是 Axios 的一个特色 Feature,我们先简单回顾下使用方式,

// 拦截器可以拦截请求或响应

// 拦截器的回调将在请求或响应的 then 或 catch 回调前被调用

var instance = axios.create(options);

var requestInterceptor = axios.interceptors.request.use(

(config) => {

// do something before request is sent

return config;

},

(err) => {

// do somthing with request error

return Promise.reject(err);

}

);

// 移除已设置的拦截器

axios.interceptors.request.eject(requestInterceptor)

那么拦截器是怎么实现的呢?

定位到源码 lib/core/Axios.js 第 14 行,

function Axios(instanceConfig) {

this.defaults = instanceConfig;

this.interceptors = {

request: new InterceptorManager(),

response: new InterceptorManager()

};

}

通过 Axios 的构造函数可以看到,拦截器 interceptors 中的 request 和 response 两者都是一个叫做 InterceptorManager 的实例,这个 InterceptorManager 是什么?

定位到源码 lib/core/InterceptorManager.js

function InterceptorManager() {

this.handlers = [];

}

InterceptorManager.prototype.use = function use(fulfilled, rejected, options) {

this.handlers.push({

fulfilled: fulfilled,

rejected: rejected,

synchronous: options ? options.synchronous : false,

runWhen: options ? options.runWhen : null

});

return this.handlers.length - 1;

};

InterceptorManager.prototype.eject = function eject(id) {

if (this.handlers[id]) {

this.handlers[id] = null;

}

};

InterceptorManager.prototype.forEach = function forEach(fn) {

utils.forEach(this.handlers, function forEachHandler(h) {

if (h !== null) {

fn(h);

}

});

};

InterceptorManager 是一个简单的事件管理器,实现了对拦截器的管理,

通过 handlers 存储拦截器,然后提供了添加,移除,遍历执行拦截器的实例方法,存储的每一个拦截器对象都包含了作为 Promise 中 resolve 和 reject 的回调以及两个配置项。

值得一提的是,移除方法是通过直接将拦截器对象设置为 null 实现的,而不是 splice 剪切数组,遍历方法中也增加了相应的 null 值处理。这样做一方面使得每一项ID保持为项的数组索引不变,另一方面也避免了重新剪切拼接数组的性能损失。

拦截器的回调会在请求或响应的 then 或 catch 回调前被调用,这是怎么实现的呢?

回到源码 lib/core/Axios.js 中第 27 行,Axios 实例对象的 request 方法,

我们提取其中的关键逻辑如下,

Axios.prototype.request = function request(config) {

// Get merged config

// Set config.method

// …

var requestInterceptorChain = [];

this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {

requestInterceptorChain.unshift(interceptor.fulfilled, interceptor.rejected);

});

var responseInterceptorChain = [];

this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {

responseInterceptorChain.push(interceptor.fulfilled, interceptor.rejected);

});

var promise;

var chain = [dispatchRequest, undefined];

Array.prototype.unshift.apply(chain, requestInterceptorChain);

chain.concat(responseInterceptorChain);

promise = Promise.resolve(config);

while (chain.length) {

promise = promise.then(chain.shift(), chain.shift());

}

return promise;

};

可以看到,当执行 request 时,实际的请求(dispatchRequest)和拦截器是通过一个叫 chain 的队列来管理的。整个请求的逻辑如下,

  1. 首先初始化请求和响应的拦截器队列,将 resolve,reject 回调依次放入队头

  2. 然后初始化一个 Promise 用来执行回调,chain 用来存储和管理实际请求和拦截器

  3. 将请求拦截器放入 chain 队头,响应拦截器放入 chain 队尾

  4. 队列不为空时,通过 Promise.then 的链式调用,依次将请求拦截器,实际请求,响应拦截器出队

  5. 最后返回链式调用后的 Promise

这里的实际请求是对适配器的封装,请求和响应数据的转换都在这里完成。

那么数据转换是如何实现的呢?

Transform data

定位到源码 lib/core/dispatchRequest.js

function dispatchRequest(config) {

throwIfCancellationRequested(config);

// Transform request data

config.data = transformData(

config.data,

config.headers,

config.transformRequest

);

var adapter = config.adapter || defaults.adapter;

return adapter(config).then(function onAdapterResolution(response) {

throwIfCancellationRequested(config);

// Transform response data

response.data = transformData(

response.data,

response.headers,

config.transformResponse

);

return response;

}, function onAdapterRejection(reason) {

if (!isCancel(reason)) {

throwIfCancellationRequested(config);

// Transform response data

if (reason && reason.response) {

reason.response.data = transformData(

reason.response.data,

reason.response.headers,

config.transformResponse

);

}

}

return Promise.reject(reason);

});

};

这里的 throwIfCancellationRequested 方法用于取消请求,关于取消请求稍后我们再讨论,可以看到发送请求是通过调用适配器实现的,在调用前和调用后会对请求和响应数据进行转换。

转换通过 transformData 函数实现,它会遍历调用设置的转换函数,转换函数将 headers 作为第二个参数,所以我们可以根据 headers 中的信息来执行一些不同的转换操作,

// 源码 core/transformData.js

function transformData(data, headers, fns) {

utils.forEach(fns, function transform(fn) {

data = fn(data, headers);

});

return data;

};

Axios 也提供了两个默认的转换函数,用于对请求和响应数据进行转换。默认情况下,

Axios 会对请求传入的 data 做一些处理,比如请求数据如果是对象,会序列化为 JSON 字符串,响应数据如果是 JSON 字符串,会尝试转换为 JavaScript 对象,这些都是非常实用的功能,

对应的转换器源码可以在 lib/default.js 的第 31 行找到,

var defaults = {

// Line 31

transformRequest: [function transformRequest(data, headers) {

normalizeHeaderName(headers, ‘Accept’);

normalizeHeaderName(headers, ‘Content-Type’);

if (utils.isFormData(data) ||

utils.isArrayBuffer(data) ||

utils.isBuffer(data) ||

utils.isStream(data) ||

utils.isFile(data) ||

utils.isBlob(data)

) {

return data;

}

if (utils.isArrayBufferView(data)) {

return data.buffer;

}

if (utils.isURLSearchParams(data)) {

setContentTypeIfUnset(headers, ‘application/x-www-form-urlencoded;charset=utf-8’);

return data.toString();

}

if (utils.isObject(data)) {

setContentTypeIfUnset(headers, ‘application/json;charset=utf-8’);

return JSON.stringify(data);

}

return data;

}],

transformResponse: [function transformResponse(data) {

var result = data;

if (utils.isString(result) && result.length) {

try {

result = JSON.parse(result);

} catch (e) { /* Ignore */ }

}

return result;

}],

}

我们说 Axios 是支持取消请求的,怎么个取消法呢?

CancelToken

其实不管是浏览器端的 xhr 或 Node.js 里 http 模块的 request 对象,都提供了 abort 方法用于取消请求,所以我们只需要在合适的时机调用 abort 就可以实现取消请求了。

那么,什么是合适的时机呢?控制权交给用户就合适了。所以这个合适的时机应该由用户决定,也就是说我们需要将取消请求的方法暴露出去,Axios 通过 CancelToken 实现取消请求,我们来一起看下它的姿势。

首先 Axios 提供了两种方式创建 cancel token,

const CancelToken = axios.CancelToken;

const source = CancelToken.source();

// 方式一,使用 CancelToken 实例提供的静态属性 source

axios.post(“/user/12345”, { name: “monch” }, { cancelToken: source.token });

source.cancel();

// 方式二,使用 CancelToken 构造函数自己实例化

let cancel;

axios.post(

“/user/12345”,

{ name: “monch” },

{

cancelToken: new CancelToken(function executor© {

cancel = c;

}),

}

);

cancel();

到底什么是 CancelToken?定位到源码 lib/cancel/CancelToken.js 第 11 行,

function CancelToken(executor) {

if (typeof executor !== “function”) {

throw new TypeError(“executor must be a function.”);

}

var resolvePromise;

this.promise = new Promise(function promiseExecutor(resolve) {

resolvePromise = resolve;

});

var token = this;

executor(function cancel(message) {

if (token.reason) {

// Cancellation has already been requested

return;

}

token.reason = new Cancel(message);

resolvePromise(token.reason);

});

}

CancelToken 就是一个由 promise 控制的极简的状态机,实例化时会在实例上挂载一个 promise,这个 promise 的 resolve 回调暴露给了外部方法 executor,这样一来,我们从外部调用这个 executor方法后就会得到一个状态变为 fulfilled 的 promise,那有了这个 promise 后我们如何取消请求呢?

是不是只要在请求时拿到这个 promise 实例,然后在 then 回调里取消请求就可以了?

定位到适配器的源码 lib/adapters/xhr.js 第 158 行,

if (config.cancelToken) {

// Handle cancellation

config.cancelToken.promise.then(function onCanceled(cancel) {

if (!request) {

return;

}

request.abort();

reject(cancel);

// Clean up request

request = null;

});

}

更多面试题

**《350页前端校招面试题精编解析大全》**内容大纲主要包括 HTML,CSS,前端基础,前端核心,前端进阶,移动端开发,计算机基础,算法与数据结构,项目,职业发展等等

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

从外部调用这个 executor方法后就会得到一个状态变为 fulfilled 的 promise,那有了这个 promise 后我们如何取消请求呢?

是不是只要在请求时拿到这个 promise 实例,然后在 then 回调里取消请求就可以了?

定位到适配器的源码 lib/adapters/xhr.js 第 158 行,

if (config.cancelToken) {

// Handle cancellation

config.cancelToken.promise.then(function onCanceled(cancel) {

if (!request) {

return;

}

request.abort();

reject(cancel);

// Clean up request

request = null;

});

}

更多面试题

**《350页前端校招面试题精编解析大全》**内容大纲主要包括 HTML,CSS,前端基础,前端核心,前端进阶,移动端开发,计算机基础,算法与数据结构,项目,职业发展等等

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

[外链图片转存中…(img-P6siAPTY-1715763438559)]

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值