mirror of
https://ghfast.top/https://github.com/zsjy/halo-theme-dream2.0-plus.git
synced 2025-03-16 12:29:41 +08:00
perf(sw): 优化调整sw中cdn的配置
This commit is contained in:
parent
9aae30eca2
commit
15869d952f
@ -720,10 +720,11 @@ spec:
|
||||
name: music_config
|
||||
label: 侧边栏音乐-参数进阶配置
|
||||
placeholder: '请输入音乐参数配置'
|
||||
value: 'list-folded="true"
|
||||
value: |-
|
||||
list-folded="true"
|
||||
server="netease"
|
||||
type="playlist"
|
||||
id="7355014621"'
|
||||
id="7355014621"
|
||||
help: '输入音乐参数配置(id / server / type必填),详细配置方式见官方文档:https://github.com/metowolf/MetingJS/'
|
||||
- $formkit: radio
|
||||
name: show_ad_tag
|
||||
@ -996,12 +997,23 @@ spec:
|
||||
options:
|
||||
- value: "false"
|
||||
label: 关闭
|
||||
- value: "&install=true"
|
||||
label: 开启CDN并发请求
|
||||
- value: "&install=true&offLine=true"
|
||||
- value: "&concurrent=true"
|
||||
label: 开启并发CDN请求
|
||||
- value: "&cache=true"
|
||||
label: 开启全站离线
|
||||
- value: "&concurrent=true&cache=true"
|
||||
label: 开启并发CDN与全站离线
|
||||
- value: "uninstall"
|
||||
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 加载
|
||||
|
185
src/js/sw.js
185
src/js/sw.js
@ -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,107 +15,81 @@
|
||||
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列表中的正则要求
|
||||
* 判断ur是否符合list列表中的要求
|
||||
*
|
||||
* @param list
|
||||
* @param url
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isExitInCacheList(list, url) {
|
||||
function isExitInUrlList(list, url) {
|
||||
return list.some(function (value) {
|
||||
return value.test(url)
|
||||
return url.indexOf(value) === 0
|
||||
})
|
||||
}
|
||||
|
||||
@ -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>|*}
|
||||
*/
|
||||
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)
|
||||
}
|
||||
}
|
||||
})()
|
||||
|
2
templates/assets/js/common.min.js
vendored
2
templates/assets/js/common.min.js
vendored
File diff suppressed because one or more lines are too long
2
templates/assets/js/sw.min.js
vendored
2
templates/assets/js/sw.min.js
vendored
@ -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})))})}))})}}();
|
@ -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">
|
||||
|
Loading…
x
Reference in New Issue
Block a user