背景
我们都听过预加载、预检请求(有时候也简称为预请求),预请求
我们先简单描述一下这个几个名词区别,避免我们在后续讨论的过程中产生歧义,给预请求下一个简单的定义
什么是接口预请求
通过预判用户的行为,提前发起请求并缓存结果,以减少用户操作时的等待时间,从而提升用户体验。这里的请求和预加载资源有所不同,预请求值得是API,API通常情况下会因为入参的不同返回值有所不同
问题分析
在开始之前通过下面的图片可以看一下我们具体优化场景,便于后续更好的讨论解决方案
上图是机票列表页接口的耗时,整整花费了6S,可以看出来,这其中的优化空间还是很大的,而且这个页面可以交互的前提是列表页渲染出来
建立模型
预请求和预加载很类似,合适的时机提前发起请求,然后存在某个地方,下一次获取时直接从缓存中获取,这是一个简单的模型,我们用一张图来表示如下:
通过上述模型我们不难发现,有一个关键点我们必须解决
如何统一劫持请求,如果不能劫持请求,我们就没办法控制请求是否走缓存
方案选型
预请求的方案并不是我们原创,事实上已经有很多技术栈开始采用这种方案来解决问题,useQuery,SWR 等提供了缓存请求结果的方案,但是这些都有一个前提是页面都采用了相同的技术栈,对于跨应用跨技术栈的场景我们需要一个更加通用的方案,毕竟将现有的请求库全部替换的RIO并不高。
以下是目前内部采用的一些方案
1、利用BFF进行缓存
在首页时,寻找合适的时机提前发送请求给BFF,BFF在拿到结果后将内容缓存到Redis 中,在下一次正式请求发起时,如果能够拿到Redis 缓存可以直接返回从而提升接口响应的速度
2、利用Native 进行缓存
这种方案比较适用于混合开发,Native+H5的方式,客户端负责劫持请求并缓存内容,当下一次重复的请求发起时可直接返回缓存
3、利用serviceWorker 进行缓存
方案 | 原理 | 优点 | 缺点 |
---|---|---|---|
Native | 通过Native劫持请求 | PC场景无法支持、需Native配合 | |
BFF | 通过BFF缓存请求 | 兼容性强,客户端无需过多的处理 | 需要BFF和客户端共同支持、前端仍然有一定的网络开销 |
ServiceWorker | 通过ServiceWorker 劫持请求 | 通过serviceWorker 劫持网络请求 纯Web技术栈支持跨平台 命中缓存时无网络开销 |
基于我们对通用方案的要求,最终我们选择了ServerWorker 作为缓存和劫持请求的基础
方案设计
设计思路
1、通过什么劫持/捕获请求?
通过ServiceWorker 劫持缓存
如果想要完成预请求,首先我们要对fetch事件有所掌控,我们可以修改全局的fetch 方法或者 统一收口 请求方法,这两种方法都免不了 对现有项目的侵入,统一请求库目前还不具备基础,servcieWorker 正好提供了这样的能力,也非常适合做请求的拦截,我们只需要对serviceWorker 稍加定制即可实现一个简单且通用的预请求方案。
通过serviceWorker 捕获请求时间
2、如何识别当前请求是预请求?
通过在请求头中添加特殊标识
在劫持请求后我们需要识别当前请求是否预请求,因为预请求和正常请求的逻辑有所区别,预请求在请求完成后需要将请求出入缓存,而正常请求需要在发起真实请求之前,查找当前是否有可用的缓存,如果有缓存,直接返回缓存,这样就加快了第二次请求的速度,也避免了多余的网路开销
在发起预请求之前,在request header 添加特殊标识,这样serviceWorker 就能知道当前的请求是否预请求
3、如何匹配缓存?
利用request的信息 type url body 计算出cacheKey,通过cacheKey 存入和读取缓存
在解决了上述两个问题后,还有一个关键问题需要解决,什么样的请求认为是同一个请求,为了确保 只有相同的请求,需要尽量保留了请求的信息,如:request body,request url,request method,所以我们的基本思路是通过以上请求信息计算cachekey,但是如果简单粗暴的这么实现任然有些问题,因为在请求中有一些特殊参数,例如GE,X-tradeid ,这类参数是为了为了更好的追踪这次请求而增加的参数,如果我们将这些参数也包含在计算cachekey的信息中,那么我们始终无法命中缓存,因为GE每一次请求都会发生变化,所以我们在计算cache 之前需要排除这些干扰信息
通过对 请求参数+URL+Type 计算出cachekey ,如果是预请求,通过cachekey存入缓存;如果是正常请求,利用cachekey查找缓存这样就能保证相同的请求命中缓存。
未完待续
以上就是基于serviceWorker 的方案设计,如果感兴趣的可以留言,在后续更新实践效果以及开源的版本