RuntimeDep

运行时函数依赖

  该配置项用于放置所有仅在浏览器 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,当尝试使用这个缓存时,检查缓存是否过期,如果过期则尝试从网络拉取最新内容。

  如果需要的话您可以通过自定义 markCacheInvalidisValidCache,将缓存失效方法改为立即删除。

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 等功能均会使用该函数判断某个请求是否成功,返回错误地返回 truefalse 会导致功能异常:

  • 对于缓存:若错误地返回了 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 竞速的功能,默认实现已经能满足大部分需求,一般情况下无需覆盖。

URL 竞速流程

fetchStandby

类型说明
(request: Request, standbyRequests: {t: number, l: () => Request[]}, optional?: RequestInit) => Promise<Response>通过备用 URL 拉取文件

  该项用于执行备用 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 中的任何内容,但是由于浏览器限制,您无法修改 refereruser-agent

  函数返回 Request 表示对当前资源启用该功能,返回任意转换为 false 的值表示禁用。