perf(sw): 优化调整sw中cdn的配置

This commit is contained in:
nineya 2023-12-26 13:59:28 +08:00
parent 9aae30eca2
commit 15869d952f
5 changed files with 133 additions and 120 deletions

View File

@ -720,10 +720,11 @@ spec:
name: music_config
label: 侧边栏音乐-参数进阶配置
placeholder: '请输入音乐参数配置'
value: 'list-folded="true"
server="netease"
type="playlist"
id="7355014621"'
value: |-
list-folded="true"
server="netease"
type="playlist"
id="7355014621"
help: '输入音乐参数配置(id / server / type必填)详细配置方式见官方文档https://github.com/metowolf/MetingJS/'
- $formkit: radio
name: show_ad_tag
@ -995,13 +996,24 @@ spec:
help: "需要在 Nginx 中添加代理配置方可启用配置方法见https://blog.nineya.com/archives/104.html"
options:
- value: "false"
label: 关闭
- value: "&install=true"
label: 开启CDN并发请求
- value: "&install=true&offLine=true"
label: 开启全站离线
label: 关闭
- value: "&concurrent=true"
label: 开启并发CDN请求
- value: "&cache=true"
label: 开启全站离线
- value: "&concurrent=true&cache=true"
label: 开启并发CDN与全站离线
- value: "uninstall"
label: 卸载
label: 卸载
- $formkit: code
name: sw_cdn_source
label: "Service Worker 并发 CDN 源"
placeholder: 请输入 CDN 地址(一行一个)
help: '填入可用的 NPM 公共开源 CDN 地址(一行一个),通过 “{CDN 地址}/{项目名}@{版本号}/${文件路径}” 可访问到文件https://unpkg.com/halo-theme-dream@3.2.1/source/js/utils.min.js'
value: |-
https://unpkg.com
https://cdn.jsdelivr.net/npm
https://npm.elemecdn.com
- $formkit: radio
name: enable_pjax
label: Pjax 加载

View File

@ -1,13 +1,8 @@
(function () {
if (self.document) {
const currentScriptUrl = document.currentScript.src
const install = new URLSearchParams(currentScriptUrl.split('?')[1]).get('install')
if (install) {
navigator.serviceWorker.register(document.currentScript.src)
.catch(function (error) {
console.log('cache failed with ' + error) // registration failed
})
} else {
const uninstall = new URLSearchParams(currentScriptUrl.split('?')[1]).get('uninstall')
if (uninstall) {
console.log('uninstall service worker.')
navigator.serviceWorker.getRegistrations().then(function (registrations) {
for (let registration of registrations) {
@ -20,117 +15,91 @@
caches.delete(key)
})
})
} else {
navigator.serviceWorker.register(document.currentScript.src)
.catch(function (error) {
console.log('cache failed with ' + error) // registration failed
})
}
} else {
//可以进行版本修改,删除缓存
const version = '1.0.0'
// 取得缓存名称
const cacheName = `Dream-${version}`
const offLine = new URLSearchParams(location.href.split('?')[1]).get('offLine')
// 取得请求参数
const envParams = new URLSearchParams(location.href.split('?')[1])
// 是否开启多cdn源并发请求
const isCdnConcurrentRequest = envParams.get('concurrent')
// 是否开启全站缓存
const isGlobalCache = envParams.get('cache')
// 主题路径
const themePath = location.origin + '/themes/dream'
// cdn源站点一行一个
const cdnSource = envParams.get('cdn').split(',').filter(item => item.length > 0 && item.indexOf('http') === 0)
// 禁止被处理(优先级别最高)
// 后端 api 不进行缓存
const notHandleList = [
location.origin + '/api',
]
// 需要走cdn和缓存的请求cdn优先于缓存
const cdnAndCacheList = [
new RegExp(`${location.origin}/themes`, 'i'), //主题目录
/\/\/(unpkg\.com|npm\.elemecdn\.com|cdn\.jsdelivr\.net\/npm)\//i, //npm公共cdn网站
themePath,
...cdnSource
]
//对这里面的请求只走缓存
//对这里面的请求只会走缓存,先缓存后下载
// jsdeliver cdn 不稳定,只走缓存
const onlyCacheList = [
new RegExp(`${location.origin}/upload`, 'i'), //图片等附件目录
/\/\/cdn.jsdelivr.net\//i, //gh目前没有可用cdn源
location.origin + '/upload',
'https://cdn.jsdelivr.net/'
]
// 不缓存不走cdn优先级别最高
const notCacheList = [
new RegExp(`${location.origin}/(admin|api)`, 'i'), //管理后台
]
const cdn = {
const cdnHandle = {
theme: {
originUrl: `${location.origin}/themes/dream`,
handleRequest: url => {
if (url.indexOf(cdn.theme.originUrl) !== 0) return
const path = url.substring(cdn.theme.originUrl.length)
if (url.indexOf(themePath) !== 0) return
const path = url.substring(themePath.length)
const version = new URLSearchParams(url.split('?')[1]).get('mew') || 'latest'
return [
url,
...cdn.npm.urlTemplates.map(value => `${value}/halo-theme-dream2.0@${version}${path}`)
...cdnSource.map(value => `${value}/halo-theme-dream@${version}${path}`)
]
},
},
npm: {
urlTemplates: [
'https://unpkg.com',
'https://cdn.jsdelivr.net/npm',
'https://npm.elemecdn.com',
],
handleRequest: url => {
return handleUrls(cdn.npm.urlTemplates, url)
for (let index in cdnSource) {
if (url.indexOf(cdnSource[index]) === 0) {
const path = url.substring(cdnSource[index].length)
return cdnSource.map(value => value + path)
}
}
}
},
}
/**
* 使用模板替换url路径
*
* @param urlTemplates
* @param url
* @returns {*}
*/
function handleUrls(urlTemplates, url) {
for (let index in urlTemplates) {
if (url.indexOf(urlTemplates[index]) === 0) {
const path = url.substring(urlTemplates[index].length)
return urlTemplates.map(value => value + path)
}
}
}
//添加缓存
self.addEventListener('install', function (event) {
console.log('install service worker.')
event.waitUntil(self.skipWaiting()) //这样会触发activate事件
})
// 激活
self.addEventListener('activate', function (event) {
console.log('service worker activate.')
const mainCache = [cacheName]
event.waitUntil(
caches.keys().then(function (cacheNames) {
return Promise.all(
cacheNames.map(function (cacheName) {
if (mainCache.indexOf(cacheName) === -1) {//没有找到该版本号下面的缓存
// When it doesn't match any condition, delete it.
console.info('version changed, clean the cache, SW: deleting ' + cacheName)
return caches.delete(cacheName)
}
})
)
})
)
return self.clients.claim()
})
/**
* 判断ur是否符合list列表中的正则要求
*
* @param list
* @param url
* @returns {boolean}
*/
function isExitInCacheList(list, url) {
* 判断ur是否符合list列表中的要求
*
* @param list
* @param url
* @returns {boolean}
*/
function isExitInUrlList(list, url) {
return list.some(function (value) {
return value.test(url)
return url.indexOf(value) === 0
})
}
/**
* 判断两个url是否属于同一个请求过滤掉部分参数
*
* @param urla
* @param urlb
* @returns {boolean}
*/
* 判断两个url是否属于同一个请求过滤掉部分参数
*
* @param urla
* @param urlb
* @returns {boolean}
*/
function isSameRequest(urla, urlb) {
// 除了这这些参数,其它的查询参数必须要一致,才认为是同一个请求
const white_query = new Set([
@ -165,18 +134,48 @@
return true
}
//添加缓存
self.addEventListener('install', function (event) {
console.log('install service worker.')
event.waitUntil(self.skipWaiting()) //这样会触发activate事件
})
// 激活
self.addEventListener('activate', function (event) {
console.log('service worker activate.')
const mainCache = [cacheName]
event.waitUntil(
caches.keys().then(function (cacheNames) {
return Promise.all(
cacheNames.map(function (cacheName) {
if (mainCache.indexOf(cacheName) === -1) {//没有找到该版本号下面的缓存
// When it doesn't match any condition, delete it.
console.info('version changed, clean the cache, SW: deleting ' + cacheName)
return caches.delete(cacheName)
}
})
)
})
)
return self.clients.claim()
})
// 拦截请求使用缓存的内容
self.addEventListener('fetch', function (event) {
if (event.request.method !== 'GET') {
// 非 get 请求不处理,被禁止处理的地址不处理
if (event.request.method !== 'GET'
|| isExitInUrlList(notHandleList, event.request.url)) {
return false
}
const isCdnAndCache = isExitInCacheList(cdnAndCacheList, event.request.url)
// 不符合缓存要求的
if (!(isCdnAndCache || isExitInCacheList(onlyCacheList, event.request.url)) || isExitInCacheList(notCacheList, event.request.url)) {
if (!offLine) { // 不需要离线
const isCdnAndCache = isExitInUrlList(cdnAndCacheList, event.request.url)
const isOnlyCacheList = isExitInUrlList(onlyCacheList, event.request.url)
// cdn并发请求未开启 或 请求没有被任何路由命中
if (!isCdnConcurrentRequest || !(isCdnAndCache || isOnlyCacheList)) {
// 不需要全站离线
if (!isGlobalCache) {
return false
}
// return false;
// 先发起请求,如果请求失败则读取离线缓存
event.respondWith(caches.open(cacheName)
.then(cache => {
return fetch(event.request)
@ -192,7 +191,7 @@
// 劫持 HTTP Request
event.respondWith(
caches.open(cacheName).then(function (cache) {
// ignoreSearch 忽略请求参数进行查找,用于匹配不同版本
// 查找缓存
return cache.match(event.request).then(function (cacheResponse) {
// 直接返回缓存
if (cacheResponse) return cacheResponse
@ -200,6 +199,7 @@
return handleRequest(event.request, isCdnAndCache)
.then((response) => {
const responseClone = response.clone()
// ignoreSearch 忽略请求参数进行查找,用于匹配不同版本
cache.matchAll(event.request, {'ignoreSearch': true})
.then(function (cache_response_list) {
// 删除旧版本的缓存文件
@ -235,21 +235,19 @@
})
/**
* 处理请求
* @param req
* @param isCdnAndCache
* @returns {Promise<Response>|*}
*/
* 处理匹配的请求
* @param req
* @param isCdnAndCache
* @returns {Promise<Response>|*}
*/
function handleRequest(req, isCdnAndCache) {
// 不是cdn缓存的话,直接进行查询并返回
if (!isCdnAndCache) return fetch(req)
// 不是cdn缓存或者未开启cdn并发,直接进行查询并返回
if (!isCdnAndCache || !isCdnConcurrentRequest) return fetch(req)
const reqUrl = req.url
// 匹配 cdn
for (const type in cdn) {
const urls = cdn[type].handleRequest(reqUrl)
if (urls) return fetchAny(reqUrl, urls)
for (const type in cdnHandle) {
const urls = cdnHandle[type].handleRequest(req.url)
if (urls) return fetchAny(req.url, urls)
}
// 没有匹配到url直接发起请求
return fetch(req)
@ -299,7 +297,10 @@
})
})
.then((res) => {
if (res.status !== 200) reject(null)
if (res.status !== 200) {
reject(res)
return
}
controller.abort() // 中断
resolve(res)
})
@ -310,8 +311,8 @@
// 判断浏览器是否支持 Promise.any
if (!Promise.any) createPromiseAny()
// 谁先返回"成功状态"则返回谁的内容,如果都返回"失败状态"则返回null
return Promise.any(PromiseAll).catch(() => null)
// 谁先返回"成功状态"则返回谁的内容
return Promise.any(PromiseAll)
}
}
})()

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
!function(){if(self.document){var e=document.currentScript.src;new URLSearchParams(e.split("?")[1]).get("install")?navigator.serviceWorker.register(document.currentScript.src).catch(function(e){console.log("cache failed with "+e)}):(console.log("uninstall service worker."),navigator.serviceWorker.getRegistrations().then(function(e){for(var t of e)t.active&&t.active.scriptURL&&-1!==t.active.scriptURL.indexOf("/sw.min.js")&&t.unregister()}),window.caches&&caches.keys&&caches.keys().then(function(e){e.forEach(function(e){console.log("delete cache",e),caches.delete(e)})}))}else{const i="Dream-1.0.0",c=new URLSearchParams(location.href.split("?")[1]).get("offLine"),o=[new RegExp(location.origin+"/themes","i"),/\/\/(unpkg\.com|npm\.elemecdn\.com|cdn\.jsdelivr\.net\/npm)\//i],a=[new RegExp(location.origin+"/upload","i"),/\/\/cdn.jsdelivr.net\//i],l=[new RegExp(location.origin+"/(admin|api)","i")],u={theme:{originUrl:location.origin+"/themes/dream",handleRequest:e=>{if(0===e.indexOf(u.theme.originUrl)){const t=e.substring(u.theme.originUrl.length),n=new URLSearchParams(e.split("?")[1]).get("mew")||"latest";return[e,...u.npm.urlTemplates.map(e=>e+"/halo-theme-dream2.0@"+n+t)]}}},npm:{urlTemplates:["https://unpkg.com","https://cdn.jsdelivr.net/npm","https://npm.elemecdn.com"],handleRequest:e=>{var t,n=u.npm.urlTemplates,r=e;for(t in n)if(0===r.indexOf(n[t])){const s=r.substring(n[t].length);return n.map(e=>e+s)}}}};function n(e,t){return e.some(function(e){return e.test(t)})}function s(e,t){var n=new Set(["mew","v","version","t","time","ts","timestamp"]),e=e.split("?"),t=t.split("?");if(e[0]===t[0]){var r=new URLSearchParams("?"+e[1]),s=new URLSearchParams("?"+t[1]);for(const e of r.keys())if(!n.has(e)&&r.get(e)!==s.get(e))return;return 1}}self.addEventListener("install",function(e){console.log("install service worker."),e.waitUntil(self.skipWaiting())}),self.addEventListener("activate",function(e){console.log("service worker activate.");const t=[i];return e.waitUntil(caches.keys().then(function(e){return Promise.all(e.map(function(e){if(-1===t.indexOf(e))return console.info("version changed, clean the cache, SW: deleting "+e),caches.delete(e)}))})),self.clients.claim()}),self.addEventListener("fetch",function(r){if("GET"!==r.request.method)return!1;const t=n(o,r.request.url);if(!t&&!n(a,r.request.url)||n(l,r.request.url))return!!c&&(r.respondWith(caches.open(i).then(t=>fetch(r.request).then(e=>(200===e.status&&t.put(r.request,e.clone()),e)).catch(()=>t.match(r.request)))),!0);r.respondWith(caches.open(i).then(function(n){return n.match(r.request).then(function(e){return e||function(e,t){if(t){var n=e.url;for(const e in u){const t=u[e].handleRequest(n);if(t){var r=n;var s=t;const i=new AbortController,c=i.signal,o=s.map(e=>new Promise(async(t,n)=>{fetch(e,{signal:c}).then(async e=>{var t=new Headers(e.headers);return t.set("service-worker-origin",r),new Response(await e.arrayBuffer(),{status:e.status,headers:t})}).then(e=>{200!==e.status&&n(null),i.abort(),t(e)}).catch(()=>n(null))}));return Promise.any||(Promise.any=function(e){return new Promise((t,n)=>{let r=(e=Array.isArray(e)?e:[]).length,s=[];if(0===r)return n(new AggregateError("All promises were rejected"));e.forEach(e=>{if(!(e instanceof Promise))return n(e);e.then(e=>t(e),e=>{r--,s.push(e),0===r&&n(new AggregateError(s))})})})}),Promise.any(o).catch(()=>null);return}}}return fetch(e)}(r.request,t).then(e=>{const t=e.clone();return n.matchAll(r.request,{ignoreSearch:!0}).then(function(e){if(e)for(const t of e){const e=t.url||t.headers.get("service-worker-origin");s(e,r.request.url)&&n.delete(e)}n.put(r.request,t)}),e}).catch(e=>(console.error(e),n.matchAll(r.request,{ignoreSearch:!0}).then(function(e){if(e)for(const t of e)if(s(t.url||t.headers.get("service-worker-origin"),r.request.url))return t})))})}))})}}();
!function(){if(self.document){var e=document.currentScript.src;new URLSearchParams(e.split("?")[1]).get("uninstall")?(console.log("uninstall service worker."),navigator.serviceWorker.getRegistrations().then(function(e){for(var t of e)t.active&&t.active.scriptURL&&-1!==t.active.scriptURL.indexOf("/sw.min.js")&&t.unregister()}),window.caches&&caches.keys&&caches.keys().then(function(e){e.forEach(function(e){console.log("delete cache",e),caches.delete(e)})})):navigator.serviceWorker.register(document.currentScript.src).catch(function(e){console.log("cache failed with "+e)})}else{const i="Dream-1.0.0",t=new URLSearchParams(location.href.split("?")[1]),o=t.get("concurrent"),c=t.get("cache"),n=location.origin+"/themes/dream",a=t.get("cdn").split(",").filter(e=>0<e.length&&0===e.indexOf("http")),u=[location.origin+"/api"],l=[n,...a],h=[location.origin+"/upload","https://cdn.jsdelivr.net/"],f={theme:{handleRequest:e=>{if(0===e.indexOf(n)){const t=e.substring(n.length),r=new URLSearchParams(e.split("?")[1]).get("mew")||"latest";return[e,...a.map(e=>e+"/halo-theme-dream@"+r+t)]}}},npm:{handleRequest:e=>{for(var t in a)if(0===e.indexOf(a[t])){const r=e.substring(a[t].length);return a.map(e=>e+r)}}}};function r(e,t){return e.some(function(e){return 0===t.indexOf(e)})}function s(e,t){var r=new Set(["mew","v","version","t","time","ts","timestamp"]),e=e.split("?"),t=t.split("?");if(e[0]===t[0]){var n=new URLSearchParams("?"+e[1]),s=new URLSearchParams("?"+t[1]);for(const e of n.keys())if(!r.has(e)&&n.get(e)!==s.get(e))return;return 1}}self.addEventListener("install",function(e){console.log("install service worker."),e.waitUntil(self.skipWaiting())}),self.addEventListener("activate",function(e){console.log("service worker activate.");const t=[i];return e.waitUntil(caches.keys().then(function(e){return Promise.all(e.map(function(e){if(-1===t.indexOf(e))return console.info("version changed, clean the cache, SW: deleting "+e),caches.delete(e)}))})),self.clients.claim()}),self.addEventListener("fetch",function(n){if("GET"!==n.request.method||r(u,n.request.url))return!1;const t=r(l,n.request.url),e=r(h,n.request.url);if(!o||!t&&!e)return!!c&&(n.respondWith(caches.open(i).then(t=>fetch(n.request).then(e=>(200===e.status&&t.put(n.request,e.clone()),e)).catch(()=>t.match(n.request)))),!0);n.respondWith(caches.open(i).then(function(r){return r.match(n.request).then(function(e){return e||function(e,t){if(t&&o)for(const t in f){var r=f[t].handleRequest(e.url);if(r){var n=e.url;const s=new AbortController,i=s.signal,c=r.map(e=>new Promise(async(t,r)=>{fetch(e,{signal:i}).then(async e=>{var t=new Headers(e.headers);return t.set("service-worker-origin",n),new Response(await e.arrayBuffer(),{status:e.status,headers:t})}).then(e=>{(200===e.status?(s.abort(),t):r)(e)}).catch(()=>r(null))}));return Promise.any||(Promise.any=function(e){return new Promise((t,r)=>{let n=(e=Array.isArray(e)?e:[]).length,s=[];if(0===n)return r(new AggregateError("All promises were rejected"));e.forEach(e=>{if(!(e instanceof Promise))return r(e);e.then(e=>t(e),e=>{n--,s.push(e),0===n&&r(new AggregateError(s))})})})}),Promise.any(c);return}}return fetch(e)}(n.request,t).then(e=>{const t=e.clone();return r.matchAll(n.request,{ignoreSearch:!0}).then(function(e){if(e)for(const t of e){const e=t.url||t.headers.get("service-worker-origin");s(e,n.request.url)&&r.delete(e)}r.put(n.request,t)}),e}).catch(e=>(console.error(e),r.matchAll(n.request,{ignoreSearch:!0}).then(function(e){if(e)for(const t of e)if(s(t.url||t.headers.get("service-worker-origin"),n.request.url))return t})))})}))})}}();

View File

@ -1,7 +1,7 @@
<head xmlns:th="https://www.thymeleaf.org" th:fragment="head"
th:with="description=${isPost ? post != null ? post.status.excerpt : singlePage != null ? singlePage.status.excerpt : site.seo.description : site.seo.description}">
<title th:text="${title + (#strings.isEmpty(site.subtitle) ? '' : '|' + site.subtitle)}"></title>
<script th:if="${theme.config.enhance.enable_sw}" th:src="${(theme.config.enhance.enable_sw == 'uninstall')? #theme.assets('/assets/js/sw.min.js') + '?mew=0.0.1' : '/sw.min.js?mew=0.0.1' + theme.config.enhance.enable_sw}"></script>
<script th:if="${theme.config.enhance.enable_sw}" th:src="${(theme.config.enhance.enable_sw == 'uninstall')? #theme.assets('/assets/js/sw.min.js?uninstall=true') + '&mew=1.2.1' : '/sw.min.js?mew=1.2.1' + theme.config.enhance.enable_sw + '&cdn=' + theme.config.enhance.sw_cdn_source.replaceAll('\n', ',')}"></script>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
<meta http-equiv="x-dns-prefetch-control" content="on">