运行时函数依赖
该配置项用于放置所有仅在浏览器 SW 环境下执行的工具函数。
对于每一项配置 <KEY>: <function>
:<KEY>
是函数名(推荐使用小写驼峰式命名),<function>
是函数体。
如果函数体中需要使用其它运行时的环境变量、函数依赖等内容,直接调用即可,如果需要避免 IDE 报错/ 警告,可以在配置文件中声明一些不导出的变量,以此假装上下文中存在该函数。 TS 还可以使用 @ts-ignore 忽略相关的错误。
例:
import {defineRuntimeDep} from 'swpp-backends'
// 该代码将在 sw. js 中创建一系列函数:
// const example = () => console. log('hello')
// function invokeExample() {
// example()
// }
defineRuntimeDep({
example: () => console. log('hello'),
invokeExample: function() {
example()
}
})
// 如果为了避免 IDE 报错,还可以在文件任意一个位置编写类似的代码:
// let example: () => void good
// let example = any 不推荐,因为丢失了类型,会影响 IDE 的自动补全和静态类型推断
// 或者直接在函数调用的位置使用 @ts-ignore 也可以避免报错,同样不推荐,理由同上
let example: () => void // 这行代码用来消除 IDE 警告
matchFromCaches
类型 | 说明 |
---|---|
(request: RequestInfo | URL) => Promise<Response |undefined> | 尝试匹配一个缓存 |
该项用于从缓存库中读取一个缓存,默认使用 Cache API (opens in a new tab)。
writeResponseToCache
类型 | 说明 |
---|---|
(request: RequestInfo | URL, response: Response, date?: boolean) => Promise<void> | 写入缓存 |
该项用于向缓存写入或更新一条数据,默认实现如下:
/**
* @param request 请求信息
* @param response 响应体
* @param date 是否存储时间戳
*/
async (request: RequestInfo | URL, response: Response, date?: boolean) => {
if (date) {
// 如果需要存储时间戳就新建一个 Response 并写入新的 header
const headers = new Headers(response.headers);
headers.set(STORAGE_TIMESTAMP, new Date().toISOString());
response = new Response(response.body, {
status: response.status,
headers
});
}
const cache = await caches.open(CACHE_NAME);
await cache.put(request, response);
};
// 用于消除 IDE 报错,值在其它配置项中插入到 SW
let CACHE_NAME: string
let STORAGE_TIMESTAMP: string
markCacheInvalid
类型 | 说明 |
---|---|
(request: RequestInfo | URL) => Promise<void> | 标记一个缓存为失效缓存 |
swpp 默认使缓存失效的方法是在响应体当中写入一个 特定的 header,当尝试使用这个缓存时,检查缓存是否过期,如果过期则尝试从网络拉取最新内容。
如果需要的话您可以通过自定义 markCacheInvalid
和 isValidCache
,将缓存失效方法改为立即删除。
isValidCache
类型 | 说明 |
---|---|
(response: Response, rule: number) => boolean | 判断缓存是否有效 |
该项用于判断一个缓存是否还有效,返回 true
将直接使用这个缓存,否则将尝试拉取新的值。
其中第二个参数是缓存的过期时间,若为负表明该缓存没有过期时间,为正时单位为毫秒,不可能为零。
如果您想要将缓存失效方法改为立即删除可以使用如下配置:
import {defineRuntimeDep} from 'swpp-backends'
defineRuntimeDep({
markCacheInvalid: (request: RequestInfo | URL) =>
caches.open(CACHE_NAME).then(cache => cache.delete(request)),
isValidCache: () => true
})
let CACHE_NAME: string
readVersion
类型 | 说明 |
---|---|
() => Promise<BrowserVersion | undefined> | 读取客户端版本号 |
该项用于读取客户端当前的版本号,默认实现使用 matchFromCaches 读取。
writeVersion
类型 | 说明 |
---|---|
(version: BrowserVersion) => Promise<void> | 更新客户端版本号 |
该项用于设置客户端当前的版本号,默认实现使用 writeResponseToCache 实现。
注意:如果您需要自定义该项目,务必在写入缓存前,为传入的 version
中的 tp
字段赋值:version.tp = Date.now()
。
postMessage
类型 | 说明 |
---|---|
(type: string, data: any, ...goals: Client[]) => Promise<void> | 向指定客户端发送消息 |
该配置项用于向指定的客户端发送消息,其中各个变量解释如下:
type
: 消息类型data
: 消息内容goals
: 客户端对象,若该项传入了一个空的数组,表明向所有客户端发送消息
isFetchSuccessful
类型 | 说明 |
---|---|
(response: Response) => boolean | 检查一个请求是否成功 |
该项用于通过 Response
检查拉取是否成功,默认通过状态码判断(若状态码返回 200
301
302
307
308
会被视为成功)。
请注意!!!缓存、URL 竞速、备用 URL 等功能均会使用该函数判断某个请求是否成功,返回错误地返回 true
或 false
会导致功能异常:
- 对于缓存:若错误地返回了
true
会导致将失败的响应缓存到本地,若错误地返回了false
将会导致结果不被缓存。 - 对于 URL 竞速和备用 URL:若错误地返回了
true
会导致将一个失败的响应视为成功响应作为最终的响应结果;若错误地返回了false
会错失正确的响应,可能导致最终响应失败。
部分 CDN 响应错误时不使用状态码进行标记,而是使用响应体中的内容,此时有下面几种解决方案:
- 避开该 CDN,使用其它 CDN 进行替代
- 对于该 CDN 的资源不启用缓存等功能
- 自定义
isFetchSuccessful
,在函数中读取响应体的数据判断请求是否成功。
需要特别注意的是,如果需要在该函数中读取 body,请先克隆响应体,然后读取克隆后的结果,否则会导致后续的流程无法读取响应体内容。
fetchWrapper
类型 | 说明 |
---|---|
(request: Request, banCache: boolean, cors: boolean, optional?: RequestInit) => Promise<Response> | 从网络拉取文件 |
一般不需要覆盖,默认实现如下:
/**
* @param request 请求信息
* @param banCache 是否禁用 HTTP 缓存
* @param cors 是否启用 cors,填 false 使用 no-cors 模式
* @param optional 请求配置
*/
(request: Request, banCache: boolean, cors: boolean, optional?: RequestInit): Promise<Response> => {
const init: RequestInit = {
referrerPolicy: request.referrerPolicy ?? '',
...optional
}
init.cache = banCache ? 'no-store' : 'default'
if (cors) {
init.mode = 'cors'
init.credentials = 'same-origin'
}
return fetch(request, init)
}
isCors
类型 | 说明 |
---|---|
(request: Request) => boolean | 判断一个资源是否使用 cors |
该项用于判断一个资源是使用 cors 还是 no-cors 模式,返回 true
表示使用 cors 模式,否则表示使用 no-cors。默认永远返回 false
。
由于 no-cors 模式下 swpp 无法读取响应体中的状态码,所以对于需要应用缓存、URL 竞速、备用 URL 的请求,全部强制使用 cors 模式。
getFastestRequests
类型 | 说明 |
---|---|
(request: Request) => Request[] | undefined | 获取竞速列表 |
该项用于启用 URL 竞速,返回任意转换为 false
的值均表示对于给定的资源不启用 URL 竞速。
注意事项:
- swpp 会严格按照返回的数组中的内容进行并发请求,如果您返回的数组中不包含原有的
Request
,swpp 将认为您希望跳过原有的资源地址。 - 开启该功能后,会较为显著地增加网络压力、流量消耗和 CPU 压力。
- 请勿滥用该功能(包括但不限于使用该功能实现类似于将网站部署到 NPM CDN 上的功能),这有违道德、容易被 CDN 封杀。
- 自定义前请阅读:#isFetchSuccessful
getStandbyRequests
类型 | 说明 |
---|---|
(request: Request) => {t: number, l: () => Request[]} | undefined | 获取备用列表 |
该项用于启用备用 URL,返回任意转换为 false
值均表示对于给定的资源不启用备用 URL。
对于返回值:
t
: 超时时间(毫秒),如果拉取原始Request
的时间超过了这个时间,将会开始并发拉取备用 URL。l
: 备用 Request 列表的 getter,设计成这样是避免在无需备用 URL 时生成列表占用 CPU、内存资源。
注意事项:
- 返回的列表中不应当包含原始
Request
,swpp 会先尝试访问原始Request
。 - 应当把最可能成功、速度最快的链接设置为原始
Request
,否则网站加载可能会出现比较大的延迟。 - 自定义前请阅读:#isFetchSuccessful
fetchFastest
类型 | 说明 |
---|---|
(list: Request[], optional?: RequestInit) => Promise<Response> | 通过竞速拉取文件 |
该项用于执行 URL 竞速的功能,默认实现已经能满足大部分需求,一般情况下无需覆盖。
fetchStandby
类型 | 说明 |
---|---|
(request: Request, standbyRequests: {t: number, l: () => Request[]}, optional?: RequestInit) => Promise<Response> | 通过备用 URL 拉取文件 |
该项用于执行备用 URL 功能,默认实现已经能满足大部分需求,一般情况下无需覆盖。
fetchFile
类型 | 说明 |
---|---|
(request: RequestInfo | URL, optional?: RequestInit) => Promise<Response> | 从网络拉取文件 |
该项是对 URL 竞速和备用 URL 的封装,如果您启用了这两个功能中的至少一个,swpp 将自动为您生成合适的 fetchFile
函数。
一般情况下无需修改该项,您的修改会完全覆盖 swpp 原有的逻辑。若您修改了该项但没有手动调用 URL 竞速和备用 URL 的接口,即使设置了相关的属性功能也无法生效。
默认实现如下:
// noinspection TypeScriptUnresolvedReference
import {defineLazyInitConfig} from 'swpp-backends'
// 在这段代码中 fetchFastestAndStandbyRequests、fetchFastestRequests 和 fetchStandbyRequests 三者
// 都是在 swpp 内部定义的三个函数,感兴趣的可以翻看 RuntimeDepCode.ts 源码
defineLazyInitConfig((runtime) => {
const runtimeDep = runtime.runtimeDep
const hasFastestRequests = runtimeDep.hasValue('getFastestRequests')
const hasStandbyRequests = runtimeDep.hasValue('getStandbyRequests')
if (hasFastestRequests && hasStandbyRequests) {
return fetchFastestAndStandbyRequests
} else if (hasFastestRequests) {
return fetchFastestRequests
} else if (hasStandbyRequests) {
return fetchStandbyRequests
} else {
return (request: RequestInfo | URL, optional?: RequestInit): Promise<Response> => {
// @ts-ignore
if (!request.url) request = new Request(request)
return fetchWrapper(request, true, true, optional)
}
}
})
isBlockRequest
类型 | 说明 |
---|---|
(request: Request) => boolean | 判断是否应当阻断请求 |
如果网站中存在需要屏蔽的请求,那么可以使用该功能阻断这些请求。若函数返回了 true
,那么 swpp 将不再通过网络拉取文件,而是直接向上级返回 204
状态码的响应体。
注意:swpp 只能阻断网络请求,而无法阻止上游处理该请求的结果。
modifyRequest
类型 | 说明 |
---|---|
(request: Request) => Request | null | undefined | 修改请求 |
如果您想要修改请求的内容,可以使用该功能返回一个新的 Request
以代替原始的 Request
。
您可以修改 Request
中的任何内容,但是由于浏览器限制,您无法修改 referer
和 user-agent
。
函数返回 Request
表示对当前资源启用该功能,返回任意转换为 false
的值表示禁用。