第一集请见 juejin.cn/post/749270...
第二集请见 juejin.cn/post/754875...
uni-app 拦截器核心流程解析以及给拦截器添加异步 resolve(false)
终止支持
1. invokeApi 函数详解
invokeApi
是 uni-app 中处理所有 API 调用的核心函数,负责协调拦截器和实际 API 执行。
javascript
function invokeApi(method, api, options, ...params) {
// 1. 获取该 API 的所有拦截器
const interceptorHooks = getApiInterceptorHooks(method);
// 2. 如果有 invoke 拦截器,调用 queue 函数处理
if (interceptorHooks && interceptorHooks.invoke) {
const res = queue(interceptorHooks.invoke, options, params);
// 3. 处理拦截器返回值
return res.then((options) => {
// 4. 执行实际 API 调用
return api(wrapperOptions(interceptorHooks, options), ...params);
});
}
// 如果没有拦截器,直接调用 API
return api(options, ...params);
}
invokeApi 执行流程详解:
- 获取拦截器 :通过
getApiInterceptorHooks(method)
获取指定 API 的所有拦截器 - 检查 invoke 拦截器 :如果有 invoke 拦截器,调用
queue
函数处理 - 处理拦截器返回值 :
- 如果拦截器返回 Promise,使用
.then()
处理解析后的值 - 如果拦截器返回 false,queue 函数会返回空对象,导致后续 API 不执行
- 如果拦截器返回 Promise,使用
- 执行实际 API:将处理后的 options 传递给实际 API 函数
- 包装选项 :通过
wrapperOptions
函数添加 success/fail/complete 拦截器
2. invoke 拦截器详解
invoke
是拦截器中最关键的钩子函数,在 API 调用前执行,可以修改参数或阻止 API 执行。
typescript
// invoke 拦截器的典型实现
uni.addInterceptor('request', {
invoke(options) {
// 1. 修改请求参数
options.url = addBaseUrl(options.url);
options.header = {
...options.header,
'Authorization': getToken()
};
// 2. 条件性阻止请求
if (options.url.includes('blocked-api')) {
return false; // 同步返回 false 阻止请求
}
// 3. 异步处理(需要修改 queue 函数支持)
return new Promise((resolve) => {
checkPermission().then(hasPermission => {
if (!hasPermission) {
resolve(false); // 异步返回 false 阻止请求
} else {
resolve(options); // 允许请求继续
}
});
});
// 4. 默认返回修改后的 options
return options;
}
});
invoke 拦截器返回值类型:
- 返回对象:修改后的参数对象,API 将使用这些参数继续执行
- 返回 false:同步阻止 API 执行,queue 函数会返回空对象
- 返回 Promise:异步处理,需要修改 queue 函数以支持 resolve(false)
- 不返回值:等同于返回 undefined,API 将使用原始参数
3. queue 函数详解
queue
函数是拦截器执行的核心机制,负责按顺序执行多个拦截器并处理返回值。
javascript
function queue(hooks, data, params) {
let promise = false;
// 遍历所有拦截器
for (let i = 0; i < hooks.length; i++) {
const hook = hooks[i];
if (promise) {
// 如果已经有 Promise,将后续 hook 包装成 Promise
promise = Promise.resolve(wrapperHook(hook, params));
}
else {
// 执行当前拦截器
const res = hook(data, params);
if (isPromise(res)) {
// 如果 hook 返回 Promise,将其作为后续处理的基础
promise = Promise.resolve(res);
// 修改:支持异步返回 false
promise = res.then(res => {
if (res === false) {
// 如果异步返回 false,返回空对象
return { then() {}, catch() {} };
}
return res;
});
}
if (res === false) {
// 如果 hook 返回 false,返回空对象阻止后续执行
return {
then() { },
catch() { },
};
}
}
}
// 返回 Promise 或带 then 方法的对象
return (promise || {
then(callback) {
return callback(data);
},
catch() { },
});
}
queue 函数执行流程详解:
- 初始化 :设置
promise = false
,表示当前没有 Promise 链 - 遍历拦截器:按顺序执行每个拦截器
- 处理拦截器返回值 :
- 同步返回 false :立即返回空对象
{then() {}, catch() {}}
,阻止后续执行 - 同步返回其他值:继续执行下一个拦截器
- 返回 Promise :
- 原始逻辑:将 Promise 作为返回值,解析后继续处理
- 修改后逻辑:检查 Promise 解析结果,如果是 false 则返回空对象
- 同步返回 false :立即返回空对象
- Promise 链处理:如果已有 Promise,将后续拦截器包装到 Promise 链中
- 返回结果 :
- 如果有 Promise 链,返回 Promise
- 否则返回带 then 方法的对象,执行回调并传入 data
4. 三者协作流程
javascript
// 1. 用户调用 API
uni.request({
url: '/api/data',
success: (res) => console.log(res)
});
// 2. uni-app 内部调用 invokeApi
invokeApi('request', actualRequestFunction, options, ...params);
// 3. invokeApi 获取拦截器并调用 queue
const interceptorHooks = getApiInterceptorHooks('request');
const res = queue(interceptorHooks.invoke, options, params);
// 4. queue 执行所有 invoke 拦截器
// - 如果拦截器返回 false,queue 返回空对象
// - 否则返回处理后的 options
// 5. invokeApi 处理 queue 返回值
return res.then((options) => {
// 6. 包装 options 添加 success/fail/complete 拦截器
const wrappedOptions = wrapperOptions(interceptorHooks, options);
// 7. 执行实际 API
return actualRequestFunction(wrappedOptions, ...params);
});
5. 拦截器终止 API 执行的机制
5.1 同步返回 false 终止机制
当拦截器的 invoke
方法同步返回 false
时:
queue
函数检测到返回值为false
- 立即返回一个空对象
{ then() {}, catch() {} }
- 在
invokeApi
函数中,调用这个空对象的then
方法 - 由于
then
方法是空的,不会执行回调 - 实际的 API 调用在回调中,因此不会执行
javascript
// invokeApi 函数中的关键代码
return res.then((options) => {
// 实际 API 调用在这里
return api(wrapperOptions(getApiInterceptorHooks(method), options), ...params);
});
5.2 异步返回 false 的问题
当拦截器的 invoke
方法异步返回 false
时:
queue
函数检测到返回值是 Promise- 将 Promise 作为返回值
- Promise 解析为
false
- 在
invokeApi
函数中,false
被当作options
参数 - 尝试将
false
传递给wrapperOptions
函数 wrapperOptions
函数尝试给false
添加属性,导致错误
javascript
// wrapperOptions 函数中的关键代码
options[name] = function callbackInterceptor(res) {
// 这里尝试给 options 添加属性,如果 options 是 false 会报错
queue(hooks, res, options).then((res) => {
return (isFunction(oldCallback) && oldCallback(res)) || res;
});
};
6. 给拦截器添加异步 resolve(false)
终止支持
6.1 修改 queue 函数
要支持异步返回 false
,需要修改 queue
函数:详见顶部PR
6.2 使用异步拦截器
修改后,可以在拦截器中异步返回 false
:
typescript
uni.addInterceptor('request', {
invoke(options) {
return new Promise((resolve) => {
// 执行异步检查
someAsyncCheck().then(shouldBlock => {
if (shouldBlock) {
// 异步返回 false 阻止请求
resolve(false);
} else {
// 正常处理请求
options.url = modifyUrl(options.url);
resolve(options);
}
});
});
}
});
7. 其他拦截器核心函数
除了 invokeApi
、invoke
和 queue
这三个核心函数外,uni-app 拦截器机制还依赖以下几个重要函数:
7.1 getApiInterceptorHooks 函数
getApiInterceptorHooks
函数负责获取指定 API 的所有拦截器钩子,包括全局拦截器和特定 API 的拦截器。
javascript
function getApiInterceptorHooks(method) {
const interceptor = Object.create(null);
// 1. 复制全局拦截器(除了 returnValue)
Object.keys(globalInterceptors).forEach((hook) => {
if (hook !== 'returnValue') {
interceptor[hook] = globalInterceptors[hook].slice();
}
});
// 2. 合并特定 API 的拦截器(除了 returnValue)
const scopedInterceptor = scopedInterceptors[method];
if (scopedInterceptor) {
Object.keys(scopedInterceptor).forEach((hook) => {
if (hook !== 'returnValue') {
interceptor[hook] = (interceptor[hook] || []).concat(scopedInterceptor[hook]);
}
});
}
return interceptor;
}
功能说明:
- 从
globalInterceptors
获取全局拦截器 - 从
scopedInterceptors[method]
获取特定 API 的拦截器 - 合并两种拦截器,特定 API 的拦截器优先级更高
- 不包含
returnValue
拦截器,因为它有单独的处理逻辑
7.2 wrapperOptions 函数
wrapperOptions
函数负责将拦截器的 success/fail/complete 钩子包装到 API 的 options 中。
javascript
function wrapperOptions(interceptors, options = {}) {
// 遍历 success/fail/complete 三种钩子
[HOOK_SUCCESS, HOOK_FAIL, HOOK_COMPLETE].forEach((name) => {
const hooks = interceptors[name];
if (!isArray(hooks)) {
return;
}
// 保存原始回调
const oldCallback = options[name];
// 创建新的回调函数
options[name] = function callbackInterceptor(res) {
// 执行所有拦截器钩子
queue(hooks, res, options).then((res) => {
// 执行原始回调
return (isFunction(oldCallback) && oldCallback(res)) || res;
});
};
});
return options;
}
功能说明:
- 为 success/fail/complete 三种回调创建拦截器链
- 保留原始回调,确保最终执行
- 使用
queue
函数按顺序执行所有钩子 - 将前一个钩子的结果传递给下一个钩子
7.3 wrapperReturnValue 函数
wrapperReturnValue
函数负责处理 API 的返回值,应用 returnValue 拦截器。
javascript
function wrapperReturnValue(method, returnValue) {
const returnValueHooks = [];
// 1. 收集全局 returnValue 拦截器
if (isArray(globalInterceptors.returnValue)) {
returnValueHooks.push(...globalInterceptors.returnValue);
}
// 2. 收集特定 API 的 returnValue 拦截器
const interceptor = scopedInterceptors[method];
if (interceptor && isArray(interceptor.returnValue)) {
returnValueHooks.push(...interceptor.returnValue);
}
// 3. 按顺序应用所有 returnValue 拦截器
returnValueHooks.forEach((hook) => {
returnValue = hook(returnValue) || returnValue;
});
return returnValue;
}
功能说明:
- 收集全局和特定 API 的 returnValue 拦截器
- 按顺序应用所有拦截器
- 每个拦截器可以修改返回值,也可以返回 undefined 保持原值
- 前一个拦截器的输出作为下一个拦截器的输入
7.4 addInterceptor 和 removeInterceptor 函数
这两个函数是 uni-app 提供给开发者使用的 API,用于添加和移除拦截器。
javascript
// 添加拦截器
const addInterceptor = defineSyncApi(API_ADD_INTERCEPTOR, (method, interceptor) => {
if (isString(method) && isPlainObject(interceptor)) {
// 添加特定 API 的拦截器
mergeInterceptorHook(scopedInterceptors[method] || (scopedInterceptors[method] = {}), interceptor);
}
else if (isPlainObject(method)) {
// 添加全局拦截器
mergeInterceptorHook(globalInterceptors, method);
}
}, AddInterceptorProtocol);
// 移除拦截器
const removeInterceptor = defineSyncApi(API_REMOVE_INTERCEPTOR, (method, interceptor) => {
if (isString(method)) {
if (isPlainObject(interceptor)) {
// 移除特定 API 的特定拦截器
removeInterceptorHook(scopedInterceptors[method], interceptor);
}
else {
// 移除特定 API 的所有拦截器
delete scopedInterceptors[method];
}
}
else if (isPlainObject(method)) {
// 移除全局拦截器
removeInterceptorHook(globalInterceptors, method);
}
}, RemoveInterceptorProtocol);
功能说明:
- 支持全局拦截器和特定 API 拦截器
- 使用
mergeInterceptorHook
和removeInterceptorHook
管理拦截器 - 通过
defineSyncApi
定义为同步 API
7.5 mergeInterceptorHook 和 removeInterceptorHook 函数
这两个辅助函数用于合并和移除拦截器钩子。
javascript
// 合并拦截器钩子
function mergeInterceptorHook(interceptors, interceptor) {
Object.keys(interceptor).forEach((hook) => {
if (isFunction(interceptor[hook])) {
interceptors[hook] = mergeHook(interceptors[hook], interceptor[hook]);
}
});
}
// 移除拦截器钩子
function removeInterceptorHook(interceptors, interceptor) {
if (!interceptors || !interceptor) {
return;
}
Object.keys(interceptor).forEach((name) => {
const hooks = interceptors[name];
const hook = interceptor[name];
if (isArray(hooks) && isFunction(hook)) {
remove(hooks, hook);
}
});
}
// 合并钩子函数
function mergeHook(parentVal, childVal) {
const res = childVal
? parentVal
? parentVal.concat(childVal)
: isArray(childVal)
? childVal
: [childVal]
: parentVal;
return res ? dedupeHooks(res) : res;
}
// 去重钩子函数
function dedupeHooks(hooks) {
const res = [];
for (let i = 0; i < hooks.length; i++) {
if (res.indexOf(hooks[i]) === -1) {
res.push(hooks[i]);
}
}
return res;
}
功能说明:
mergeInterceptorHook
将新拦截器合并到现有拦截器集合中removeInterceptorHook
从拦截器集合中移除特定拦截器mergeHook
处理钩子函数的合并逻辑dedupeHooks
确保同一个钩子函数不会被重复添加
7.6 promisify 函数
promisify
函数负责将回调风格的 API 转换为 Promise 风格,同时应用拦截器。
javascript
function promisify(name, fn) {
return (args = {}, ...rest) => {
if (hasCallback(args)) {
// 如果有回调,直接调用 invokeApi
return wrapperReturnValue(name, invokeApi(name, fn, extend({}, args), rest));
}
// 如果没有回调,创建 Promise 并调用 invokeApi
return wrapperReturnValue(name, handlePromise(new Promise((resolve, reject) => {
invokeApi(name, fn, extend({}, args, { success: resolve, fail: reject }), rest);
})));
};
}
功能说明:
- 检测是否提供了回调函数
- 如果有回调,使用回调风格调用 API
- 如果没有回调,使用 Promise 风格调用 API
- 无论哪种风格,都应用
wrapperReturnValue
处理返回值
7.7 拦截器存储结构
uni-app 使用两个全局对象存储拦截器:
javascript
// 全局拦截器,对所有 API 生效
const globalInterceptors = {};
// 特定 API 的拦截器,只对指定 API 生效
const scopedInterceptors = {};
拦截器类型:
invoke
:API 调用前拦截success
:API 成功回调拦截fail
:API 失败回调拦截complete
:API 完成回调拦截returnValue
:API 返回值拦截
8. 总结
uni-app 拦截器机制由多个函数协同工作,除了核心的 invokeApi
、invoke
和 queue
函数外,还包括:
- getApiInterceptorHooks:获取 API 的所有拦截器
- wrapperOptions:包装 API 选项,添加回调拦截器
- wrapperReturnValue:处理 API 返回值,应用返回值拦截器
- addInterceptor/removeInterceptor:提供给开发者使用的 API
- mergeInterceptorHook/removeInterceptorHook:管理拦截器的辅助函数
- promisify:将回调风格 API 转换为 Promise 风格
- globalInterceptors/scopedInterceptors:存储拦截器的全局对象
这些函数共同构成了 uni-app 强大的拦截器系统,支持在 API 调用的各个阶段进行拦截和处理,为开发者提供了灵活的扩展能力。通过理解这些函数的工作原理,我们可以更好地利用拦截器机制,实现复杂的功能需求。