2024-05-07 12:31:42 +08:00

1 line
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(()=>{var __webpack_modules__={"./src/js/sw.js":()=>{eval("(function () {\r\n if (self.document) {\r\n const currentScriptUrl = document.currentScript.src\r\n const uninstall = new URLSearchParams(currentScriptUrl.split('?')[1]).get('uninstall')\r\n if (uninstall) {\r\n console.log('uninstall service worker.')\r\n navigator.serviceWorker.getRegistrations().then(function (registrations) {\r\n for (let registration of registrations) {\r\n registration.active && registration.active.scriptURL && registration.active.scriptURL.indexOf('/sw.min.js') !== -1 && registration.unregister()\r\n }\r\n })\r\n window.caches && caches.keys && caches.keys().then(function (keys) {\r\n keys.forEach(function (key) {\r\n console.log('delete cache', key)\r\n caches.delete(key)\r\n })\r\n })\r\n } else {\r\n navigator.serviceWorker.register(document.currentScript.src)\r\n .catch(function (error) {\r\n console.log('cache failed with ' + error) // registration failed\r\n })\r\n }\r\n } else {\r\n //可以进行版本修改,删除缓存\r\n const version = '1.0.0'\r\n // 取得缓存名称\r\n const cacheName = `Dream2-plus-${version}`\r\n // 取得请求参数\r\n const envParams = new URLSearchParams(location.href.split('?')[1])\r\n // 是否开启多cdn源并发请求\r\n const isCdnConcurrentRequest = envParams.get('concurrent')\r\n // 是否开启全站缓存\r\n const isGlobalCache = envParams.get('cache')\r\n // 主题路径\r\n const themePath = location.origin + '/themes/theme-dream2-plus'\r\n // cdn源站点一行一个\r\n const cdnSource = envParams.get('cdn').split(',').filter(item => item.length > 0 && item.indexOf('http') === 0)\r\n\r\n // 禁止被处理(优先级别最高)\r\n // 后端 api 不进行缓存\r\n const notHandleList = [\r\n location.origin + '/api',\r\n ]\r\n\r\n // 需要走cdn和缓存的请求cdn优先于缓存\r\n const cdnAndCacheList = [\r\n themePath,\r\n ...cdnSource\r\n ]\r\n\r\n //对这里面的请求只会走缓存,先缓存后下载\r\n // jsdeliver cdn 不稳定,只走缓存\r\n const onlyCacheList = [\r\n location.origin + '/upload',\r\n 'https://cdn.jsdelivr.net/'\r\n ]\r\n\r\n const cdnHandle = {\r\n theme: {\r\n handleRequest: url => {\r\n if (url.indexOf(themePath) !== 0) return\r\n const path = url.substring(themePath.length)\r\n const version = new URLSearchParams(url.split('?')[1]).get('mew') || 'latest'\r\n return [\r\n url,\r\n ...cdnSource.map(value => `${value}/halo-theme-dream2-plus@${version}${path}`)\r\n ]\r\n },\r\n },\r\n npm: {\r\n handleRequest: url => {\r\n for (let index in cdnSource) {\r\n if (url.indexOf(cdnSource[index]) === 0) {\r\n const path = url.substring(cdnSource[index].length)\r\n return cdnSource.map(value => value + path)\r\n }\r\n }\r\n }\r\n },\r\n }\r\n\r\n /**\r\n * 判断ur是否符合list列表中的要求\r\n *\r\n * @param list\r\n * @param url\r\n * @returns {boolean}\r\n */\r\n function isExitInUrlList(list, url) {\r\n return list.some(function (value) {\r\n return url.indexOf(value) === 0\r\n })\r\n }\r\n\r\n /**\r\n * 判断两个url是否属于同一个请求过滤掉部分参数\r\n *\r\n * @param urla\r\n * @param urlb\r\n * @returns {boolean}\r\n */\r\n function isSameRequest(urla, urlb) {\r\n // 除了这这些参数,其它的查询参数必须要一致,才认为是同一个请求\r\n const white_query = new Set([\r\n 'mew', // 自定义的版本号\r\n 'v',\r\n 'version',\r\n 't',\r\n 'time',\r\n 'ts',\r\n 'timestamp'\r\n ])\r\n\r\n const a_url = urla.split('?')\r\n const b_url = urlb.split('?')\r\n if (a_url[0] !== b_url[0]) {\r\n return false\r\n }\r\n\r\n const a_params = new URLSearchParams('?' + a_url[1])\r\n const b_params = new URLSearchParams('?' + b_url[1])\r\n\r\n // 显示所有的键\r\n for (const key of a_params.keys()) {\r\n if (white_query.has(key)) {//对于版本号的key 忽略\r\n continue\r\n }\r\n if (a_params.get(key) !== b_params.get(key)) {//其它key的值必须相等比如type=POST 这种\r\n return false\r\n }\r\n }\r\n\r\n return true\r\n }\r\n\r\n //添加缓存\r\n self.addEventListener('install', function (event) {\r\n console.log('install service worker.')\r\n event.waitUntil(self.skipWaiting()) //这样会触发activate事件\r\n })\r\n\r\n // 激活\r\n self.addEventListener('activate', function (event) {\r\n console.log('service worker activate.')\r\n const mainCache = [cacheName]\r\n event.waitUntil(\r\n caches.keys().then(function (cacheNames) {\r\n return Promise.all(\r\n cacheNames.map(function (cacheName) {\r\n if (mainCache.indexOf(cacheName) === -1) {//没有找到该版本号下面的缓存\r\n // When it doesn't match any condition, delete it.\r\n console.info('version changed, clean the cache, SW: deleting ' + cacheName)\r\n return caches.delete(cacheName)\r\n }\r\n })\r\n )\r\n })\r\n )\r\n return self.clients.claim()\r\n })\r\n\r\n // 拦截请求使用缓存的内容\r\n self.addEventListener('fetch', function (event) {\r\n // 非 get 请求不处理,被禁止处理的地址不处理\r\n if (event.request.method !== 'GET'\r\n || isExitInUrlList(notHandleList, event.request.url)) {\r\n return false\r\n }\r\n const isCdnAndCache = isExitInUrlList(cdnAndCacheList, event.request.url)\r\n const isOnlyCacheList = isExitInUrlList(onlyCacheList, event.request.url)\r\n // cdn并发请求未开启 或 请求没有被任何路由命中\r\n if (!isCdnConcurrentRequest || !(isCdnAndCache || isOnlyCacheList)) {\r\n // 不需要全站离线\r\n if (!isGlobalCache) {\r\n return false\r\n }\r\n // 先发起请求,如果请求失败则读取离线缓存\r\n event.respondWith(caches.open(cacheName)\r\n .then(cache => {\r\n return fetch(event.request)\r\n .then((response) => {\r\n if (response.status === 200) cache.put(event.request, response.clone())\r\n return response\r\n })\r\n .catch(() => cache.match(event.request))\r\n })\r\n )\r\n return true\r\n }\r\n // 劫持 HTTP Request\r\n event.respondWith(\r\n caches.open(cacheName).then(function (cache) {\r\n // 查找缓存\r\n return cache.match(event.request).then(function (cacheResponse) {\r\n // 直接返回缓存\r\n if (cacheResponse) return cacheResponse\r\n\r\n return handleRequest(event.request, isCdnAndCache)\r\n .then((response) => {\r\n const responseClone = response.clone()\r\n // ignoreSearch 忽略请求参数进行查找,用于匹配不同版本\r\n cache.matchAll(event.request, {'ignoreSearch': true})\r\n .then(function (cache_response_list) {\r\n // 删除旧版本的缓存文件\r\n if (cache_response_list) {\r\n for (const cache_response of cache_response_list) {\r\n const responseUrl = cache_response.url || cache_response.headers.get('service-worker-origin')\r\n if (isSameRequest(responseUrl, event.request.url)) {\r\n cache.delete(responseUrl)\r\n }\r\n }\r\n }\r\n cache.put(event.request, responseClone)\r\n })\r\n return response\r\n })\r\n .catch(error => {\r\n console.error(error)\r\n return cache.matchAll(event.request, {'ignoreSearch': true})\r\n .then(function (cache_response_list) {\r\n // 从缓存中取得历史版本的文件\r\n if (cache_response_list) {\r\n for (const cache_response of cache_response_list) {\r\n if (isSameRequest(cache_response.url || cache_response.headers.get('service-worker-origin'), event.request.url)) {\r\n return cache_response\r\n }\r\n }\r\n }\r\n })\r\n })\r\n })\r\n })\r\n )\r\n })\r\n\r\n /**\r\n * 处理匹配的请求\r\n * @param req\r\n * @param isCdnAndCache\r\n * @returns {Promise<Response>|*}\r\n */\r\n function handleRequest(req, isCdnAndCache) {\r\n // 不是cdn缓存或者未开启cdn并发直接进行查询并返回\r\n if (!isCdnAndCache || !isCdnConcurrentRequest) return fetch(req)\r\n\r\n // 匹配 cdn\r\n for (const type in cdnHandle) {\r\n const urls = cdnHandle[type].handleRequest(req.url)\r\n if (urls) return fetchAny(req.url, urls)\r\n }\r\n // 没有匹配到url直接发起请求\r\n return fetch(req)\r\n }\r\n\r\n // Promise.any 的 polyfill\r\n function createPromiseAny() {\r\n Promise.any = function (promises) {\r\n return new Promise((resolve, reject) => {\r\n promises = Array.isArray(promises) ? promises : []\r\n let len = promises.length\r\n let errs = []\r\n if (len === 0)\r\n return reject(new AggregateError('All promises were rejected'))\r\n promises.forEach((p) => {\r\n if (!(p instanceof Promise)) return reject(p)\r\n p.then(\r\n (res) => resolve(res),\r\n (err) => {\r\n len--\r\n errs.push(err)\r\n if (len === 0) reject(new AggregateError(errs))\r\n }\r\n )\r\n })\r\n })\r\n }\r\n }\r\n\r\n // 发送所有请求\r\n function fetchAny(originUrl, urls) {\r\n // 中断一个或多个请求\r\n const controller = new AbortController()\r\n const signal = controller.signal\r\n\r\n // 遍历将所有的请求地址转换为promise\r\n const PromiseAll = urls.map((url) => {\r\n // eslint-disable-next-line no-async-promise-executor\r\n return new Promise(async (resolve, reject) => {\r\n fetch(url, {signal})\r\n .then(async res => { // 重新封装响应\r\n const newHeaders = new Headers(res.headers)\r\n newHeaders.set('service-worker-origin', originUrl)\r\n return new Response(await res.arrayBuffer(), {\r\n status: res.status,\r\n headers: newHeaders,\r\n })\r\n })\r\n .then((res) => {\r\n if (res.status !== 200) {\r\n reject(res)\r\n return\r\n }\r\n controller.abort() // 中断\r\n resolve(res)\r\n })\r\n .catch(() => reject(null)) // 去除中断的错误信息\r\n })\r\n })\r\n\r\n // 判断浏览器是否支持 Promise.any\r\n if (!Promise.any) createPromiseAny()\r\n\r\n // 谁先返回\"成功状态\"则返回谁的内容\r\n return Promise.any(PromiseAll)\r\n }\r\n }\r\n})()\r\n\n\n//# sourceURL=webpack://halo-theme-dream2.0-plus/./src/js/sw.js?")}},__webpack_exports__={};__webpack_modules__["./src/js/sw.js"]()})();