博客最近经常出现部分地区CSS/JS资源异常,如何做异常检测和切换呢?

刚好之前处理过类似的冷门需求,🥶,简单记录一下。

回到这个问题,本质上是将异常的资源路径转发到可以正常访问的路径。

首先肯定是去检测异常资源有哪些, 然后进行下一步转发

由此思考的两种浏览器客户端方案:

  1. Ajax 解析HTML,对资源进行替换转发。
  2. ServiceWorker 拦截路径转发。

第一种方案要考虑的点比较多,带来各种繁琐的正则,下面是一种使用 ServiceWorker 来实现代理的方案。

image-20230808113132892

就不贴完整代码了,简单阐述下思路:

1
2
3
4
5
6
7
# 页面注入的脚本 
/**
* 0. 检查是否安装SW、是否需要更新卸载SW
* 1. 注入全局异常资源检测
* 2. 判断异常资源是否存在备用资源,缓存异常资源数据,并设置缓存过期机制,刷新数据
* 3. 是否需要激活SW进行异常资源转发,并刷新页面应用SW
*/

在SW脚本中,则是拦截 fetch,在生命周期的钩子与页面脚本通信即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# SW脚本
self.addEventListener('fetch',(event) => {
// 获取并更新 caches 缓存
getData()

// TODO 清除缓存逻辑

const {
FailedCDNs = [],
BackCDNs = {}
} = data || {}
log('(Cache)', data)

const url = new URL(event.request.url)
const domain = url.hostname;
log(`(Fetch) ${domain} -- ${url}`)

if(FailedCDNs.includes(domain) && BackCDNs[domain]) {
url.hostname = BackCDNs[domain];
log(`(Proxy) ${domain} -- ${url}`)
if (!ProxyCDNS.includes(domain)) {
ProxyCDNS.push(domain)
setCacheShareData(ProxyCDNS)
}
event.respondWith(fetch(url))
}
})

值得一提是的,页面与SW之前的通信,数据的缓存可以直接通过 caches 来进行串联。

下面是读取、写入 caches 的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 根据 Key 读取缓存
* @param key
* @return {Promise<any>}
*/
async function getCacheShareData (key = 'C_KEY_2_SW') {
return await caches.open(key).then(async cache => {
const res = await cache.match('/C_DATA')
const data = await res?.json() || {}
return data
});
}

/**
* 写入缓存
* @param data
* @param key
*/
function setCacheShareData (data, key = 'C_KEY_2_PAGE') {
caches.open(key).then(cache => {
cache.put('/C_DATA', new Response(JSON.stringify(data)))
})
}