1. fetch() 很好,但你想要更好的
fetch()
API 使你能够在 Web 应用程序中执行网络申请。
fetch()
的用法很间接:调用 fetch('/movies.json’)
来启动申请,申请实现后,你将取得一个 Response 对象,你能够从中提取数据。
这是一个简略的示例,阐明如何从 /movies.json
URL 获取 JSON 格局的电影:
async function executeRequest() {const response = await fetch('/movies.json');
const moviesJson = await response.json();
console.log(moviesJson);
}
executeRequest();
// logs [{name: 'Heat'}, {name: 'Alien'}]
如下面的代码片段所示,你必须手动从响应中提取 JSON 对象:moviesJson = await response.json()
。只做一次,不是问题,然而如果你的应用程序执行了许多申请,那么应用 await response.json()
来提取所有的 JSON 对象的时候就会很乏味。
因而很想应用第三方库,比方 axios,大大简化了申请的解决,思考到同样应用 axios 来获取电影。
async function executeRequest() {const moviesJson = await axios('/movies.json');
console.log(moviesJson);
}
executeRequest();
// logs [{name: 'Heat'}, {name: 'Alien'}]
moviesJson = await axios('/movies.json’)
返回理论的 JSON 响应,你无需像 fetch()
一样手动提取 JSON。
然而……应用诸如 axios 之类的帮忙程序库会带来一系列问题。
首先,它减少了你的 Web 利用的捆绑大小。其次,你的应用程序与第三方库联合在一起:你失去了该库的所有益处,但也失去了它的所有 bug。
我打算采纳一种从两种状况中都吸取最大好处的办法 - 应用装璜器模式来进步 fetch()
API 的易用性和灵活性。
在下一节中,咱们将介绍如何做到这一点。
2. 筹备 Fetcher 接口
装璜器模式很有用,因为它容许以灵便和涣散耦合的形式在根本逻辑之上增加性能(换句话说,装璜)。
如果你对装璜器模式不相熟,倡议浏览一下它的工作原理。
利用装璜器来加强 fetch()
须要几个简略的步骤。
第一步是申明一个名为 Fetcher
的形象接口:
type ResponseWithData = Response & {data?: any};
interface Fetcher {
run(
input: RequestInfo,
init?: RequestInit
): Promise<ResponseWithData>;
}
Fetcher
接口只有一个办法,承受雷同的参数,并返回与惯例 fetch()
雷同类型的数据。
第二步是实现根本的 fetcher 类:
class BasicFetcher implements Fetcher {
run(
input: RequestInfo,
init?: RequestInit
): Promise<ResponseWithData> {return fetch(input, init);
}
}
BasicFetcher
实现了 Fetcher
接口,它的一个办法 run()
调用了惯例的 fetch()
函数,简略得很。
让咱们应用根本的 fetcher 类来获取电影列表:
const fetcher = new BasicFetcher();
const decoratedFetch = fetcher.run.bind(fetcher);
async function executeRequest() {const response = await decoratedFetch('/movies.json');
const moviesJson = await response.json();
console.log(moviesJson);
}
executeRequest();
// logs [{name: 'Heat'}, {name: 'Alien'}]
Try the demo
const fetcher = new BasicFetcher()
创立一个 fetcher 类的实例,decoratedFetch = fetcher.run.bind(fetcher)
创立一个绑定办法。
而后你能够应用 decoratedFetch('/movies.json’)
来获取电影 JSON,就像应用惯例的 fetch()
一样。
在这一步,BasicFetcher
类并没有带来益处,而且,因为有了新的接口和新的类,事件变得更加简单。稍等 … 将装璜器引入动作后,你将看到微小的益处。
3. JSON 提取装璜器
装璜器模式的主力军是装璜器类。
装璜类必须合乎 Fetcher
接口,包装被装璜的实例,并在 run()
办法中引入额定的性能。
让咱们实现一个装璜器,该装璜器从响应对象中提取 JSON 数据:
class JsonFetcherDecorator implements Fetcher {
private decoratee: Fetcher;
constructor (decoratee: Fetcher) {this.decoratee = decoratee;}
async run(
input: RequestInfo,
init?: RequestInit
): Promise<ResponseWithData> {const response = await this.decoratee.run(input, init);
const json = await response.json();
response.data = json;
return response;
}
}
让咱们认真看看 JsonFetcherDecorator
是如何结构的。
JsonFetcherDecorator
合乎 Fetcher
接口。
JsonExtractorFetch
有一个公有字段 decoratee
,它也合乎 Fetcher
接口。在 run()
办法外面,this.decoratee.run(input, init)
能够理论获取数据。
而后,json = await response.json()
从响应中提取 JSON 数据。最初,response.data = json
将提取的 JSON 数据调配给响应对象。
当初让咱们用 JsonFetcherDecorator
装璜器组成装璜 BasicFetcher
,并简化 fetch()
的应用。
const fetcher = new JsonFetcherDecorator(new BasicFetcher()
);
const decoratedFetch = fetcher.run.bind(fetcher);
async function executeRequest() {const { data} = await decoratedFetch('/movies.json');
console.log(data);
}
executeRequest();
// logs [{name: 'Heat'}, {name: 'Alien'}]
Try the demo
当初,不必再从响应中手动提取 JSON 数据,你能够从响应对象的 data
属性中拜访提取的数据。
通过将 JSON 提取器移至装璜器,当初在每个应用 const {data} = decoratedFetch(URL)
的中央,你都不用手动提取 JSON 对象。
4. 申请超时装璜器
fetch()
API 默认在浏览器指定的工夫超时,在 Chrome 浏览器中,网络申请的超时工夫为 300 秒,而在 Firefox 浏览器中则为 90 秒。
用户最多能够期待 8 秒的工夫来实现简略的申请,所以须要对网络申请设置超时,8 秒后告知用户网络问题。
装璜器模式的益处是,你能够用任意多的装璜器来装璜你的根本实现。所以,让咱们为 fetch 申请创立一个超时装璜器。
const TIMEOUT = 8000; // 8 seconds
class TimeoutFetcherDecorator implements Fetcher {
private decoratee: Fetcher;
constructor(decoratee: Fetcher) {this.decoratee = decoratee;}
async run(
input: RequestInfo,
init?: RequestInit
): Promise<ResponseWithData> {const controller = new AbortController();
const id = setTimeout(() => controller.abort(), TIMEOUT);
const response = await this.decoratee.run(input, {
...init,
signal: controller.signal
});
clearTimeout(id);
return response;
}
}
TimeoutFetcherDecorator
是一个实现 Fetcher
接口的装璜器。
在 TimeoutFetcherDecorator
的 run()
办法外面:如果在 8 秒内还没有实现申请,就用 AbortController 来停止申请。
当初,让这个装璜器开始工作:
const fetcher = new TimeoutFetcherDecorator(
new JsonFetcherDecorator(new BasicFetcher()
)
);
const decoratedFetch = fetcher.run.bind(fetcher);
async function executeRequest() {
try {const { data} = await decoratedFetch('/movies.json');
console.log(data);
} catch (error) {
// 如果申请超时超过 8 秒
console.log(error.name);
}
}
executeRequest();
// 如果申请耗时超过 8 秒
// 打印 "AbortError"
Try the demo
在 demo 中,对 /movies.json
的申请耗时超过 8 秒。
因为 TimeoutFetcherDecorator
的缘故,decoratedFetch('/ movies.json’)
引发超时谬误。
当初,根本的 fetcher 被包裹在 2 个装璜器中:一个提取 JSON 对象,另一个在 8 秒内超时完结申请,这极大地简化了 decoratedFetch()
的应用。
5. 总结
fetch()
API 提供了执行获取申请的基本功能,但你须要的不止这些,应用 fetch()
将强制你手动从申请中提取 JSON 数据、配置超时等等。
为了防止模板,你能够应用一个更敌对的库,比方 axios。然而,应用像 axios 这样的第三方库会减少应用程序的捆绑大小,以及你与它的严密耦合。
另一种解决方案是在 fetch()
之上利用装璜器模式。你能够制作装璜器,从申请中提取 JSON,超时申请等等。你能够随时组合、增加或删除装璜器,而不影响应用装璜的 fetch 的代码。
你想把 fetch()
和最常见的装璜器一起应用吗?我为你创立了要点!能够在你的应用程序中随便应用它,甚至能够依据本人的须要增加更多装璜器!
还有什么其余的 fetch()
装璜器可能有用?请在上面的评论中写下你的实现 (请应用 TypeScript)