共计 2190 个字符,预计需要花费 6 分钟才能阅读完成。
引子
在后面界面开发的过程中,为了加强在与后端交互过程中的用户体验,通常会显示 Loading 动画。Loading 动画会在与后端交互完结的时候敞开。这是一个很惯例的需要,技术实现也不简单。
showLoading();
axios.request(...)
.then(...)
.finally(() => hideLoading());
Node.js 和大部分浏览器都在 2018 年实现了对 Promise.prototype.finally()
的反对。Deno 在 2020 年公布的 1.0 中也曾经反对 finally()
了。即便不反对,应用 await
也很容易解决。
showLoading()
try {await axios.request(...);
}
finally {hideLoading();
}
而在更早的时候,jQuery 在 jqXHR 中就曾经通过 always()
提供了反对。
showLoading();
$.ajax(...)
.done(...)
.always(() => hideLoading());
拦截器中的 Loading … done 逻辑
接下来,为了所有接口调用的行为统一,也为了在一个中央解决雷同的事件以达到复用的目标,Loading … done 的逻辑开始被写在一些拦截器中。这对单个近程接口调用来说,没有问题。但如果有这样一个业务逻辑会怎么样:
function async doSomething() {const token = await fetchToken();
const auth = await remoteAuth(token);
const result = await fetchBusiness(auth);
}
假如下面的每个调用都应用了 Axios,而 Axios 在拦截器中注入了 showLoading()
和 hideLoading()
的逻辑。那么这段代码会顺次弹出三个 Loading 动画。一个业务弹多个 Loading 动画的确是个不太好的体验。
给 Loading 记数
其实这个问题咱们能够在 showLoading()
和 hideLoading()
中去想方法。咱们把这两个办法放入一个闭包环境,而后用一个变量来记录调用次数:
const {showLoading, hideLoading} = (() => {
let count = 0;
function showLoading() {
count++;
if (count > 1) {return;}
// TODO show loading view
}
function hideLoading() {
count--;
if (count > 1) {return;}
// TODO hide loading view
}
})();
包装业务逻辑代替拦截器计划
作者观点
我集体并不同意在拦截器里去解决界面上的事件。拦截器中应该解决与申请自身强相干的事件,比方对参数的预处理,对响应的后处理等。
我不太同意在拦截器中去解决界面上的货色。像这种状况,能够设计一个 wrap 函数来解决 Loading 的出现并调用通过参数传入的业务逻辑。这个 wrap 函数能够这样写:
async function wrapLoading(fn) {showLoading();
try {return await fn();
}
finally {hideLoading();
}
}
在应用的时候能够这样用:
// 单个近程调用,不带参数
await wrapLoading(fetchSomething);
// 单个近程调用,带参数
await wrapLoading(() => fetchSomething(arg1, arg2, arg3));
// 多个调用的组合逻辑
const result = await wrapLoading(() => {const token = await fetchToken();
const auth = await remoteAuth(token);
return await fetchBusiness(auth);
});
下沉包装函数升高业务解决复杂度
为了利用内更自在地统一化解决,倡议对底层 Ajax 框架进行一次封装。业务近程调用时应用封装的接口,防止间接应用 Ajax 库接口。比方对 Axios request 进行一层封装。
async function request(url, config) {
config.url = url;
return await axios.request(config);
}
如果须要显示 Loading,能够扩大 config
,加一个 withLoading
选项:
async function request(url, config) {const { withLoading, ...cfg} = config;
cfg.url = url;
if (!withLoading) {return await axios.request(cfg); }
try {showLoading();
return await axios.request(cfg);
}
finally {hideLoading();
}
}
如果扩大的业务参数比拟多,能够思考封装成一个对象,比方 config.options
,也能够给封装的 request
多加一个参数:request(url, config, options)
,这些实现都不难,就不细说了。
有了这层封装之后,如果当前想更换 Ajax 框架也绝对容易,只须要批改封装的 request
函数即可,做到了业务层与框架 / 工具的解耦。