提升文章速度(多镜像)
归介绍
一个基于规则驱动的前端路由拦截器
用规则定义黑科技
ClientWorker 能干什么?
- 绕备,在域名不变动的情况下,其余用户所有请求均可以定向到你的其他服务器或者 cdn,而首屏域名无需 ICP 备案。
- 降本,你可以用廉价的家宽+公网 ipv4/ipv6,即使是 80/443 被封锁,你也可以在不变动端口的情况下将用户流量引向家宽。
- 白嫖,可以用免费的公网穿透服务,接近零成本托管你的服务。
- 加速,将静态资源流量(乃至动态资源)并发到全球 cdn,实现前端级负载均衡。
- 绕禁,通过在前端修改标头的方式,修复被故意篡改的 MIME,正常托管网站,绕过各大托管商对于网站部署的限制,可以毫无负担的使用阿里云、腾讯云等对象存储而不用开启网站模式,乃至 GithubRaw 无限流量(绕过 GithubPage 100GB 限制)。
- 愈合:通过并发方式,辅助 JSDelivr、Unpkg、cdnjs 等大陆几乎不可达请求重定向至其他 cdn,从而实现无修改、全球加速。
- 不宕,即使首屏服务器离线或不可达,已访问过的用户依旧可以正常命中备用服务器。
- 缓存,颗粒化控制缓存,多种情况不同选择,智能调度缓存和请求,避免有缓存时无返回、缓存无法及时更新问题,确保缓存在客户端工作的更顺畅。
- 离线,可以迅速支撑普通离线应用,助力快速构建 PWA。
- 兼容,Webp 无缝,可以通过判断标头来判断是否支持 Webp,并且自动替换图片请求,为网站加速助力。
- 审核,通过内置的规则可以屏蔽并替换、拦截敏感词汇,实现网站内容安全。
- 无刷,你不需要刷新就可以激活 ClientWorker
- 热更,即使源站完全宕机,你也可以更新用户手中的 ClientWorker 与配置,确保网站正常运行。
- 切片,对于一个请求发起多个切片以提高单文件下载速度
- 叠速,专门为 ClientWorker 开发的 KFCThursdayVW50 引擎能在浏览器端切片并同时并发不同的镜像服务器,对于下载大文件可以带宽叠加的效果。
- 均衡,对多个镜像并发,选择最优的镜像服务器,保证网站的响应速度,同时达到负载均衡的目的。
- 高度自定义…更多玩法等你挖掘
使用
创建文件
在网站source
目录下创建cw.js
文件
外部链接
1 | importScripts("https://lib.baomitu.com/clientworker/latest/dist/cw.js"); |
1 | importScripts( |
1 | importScripts("https://cdn.jsdelivr.net/npm/clientworker@latest"); //最好指定版本 |
本地
注意: 本文在此的文件为3.0版本,请及时更新
1 | (()=>{var __webpack_modules__={385:e=>{e.exports=function(){function e(e,t){this.namespace=e||"CacheDBDefaultNameSpace",this.prefix=t||"CacheDBDefaultPrefix",this.read=async function(e,t){return t=t||{type:"text"},new Promise(((n,r)=>{caches.open(this.namespace).then((r=>{r.match(new Request(`https://${this.prefix}/${encodeURIComponent(e)}`)).then((e=>{switch(t.type){case"json":return void n(e.json());case"arrayBuffer":return void n(e.arrayBuffer());case"blob":return void n(e.blob());default:return void n(e.text())}})).catch((e=>{n(null)}))}))}))},this.write=async function(e,t,n){return n=n||{type:"text"},new Promise(((r,i)=>{caches.open(this.namespace).then((i=>{i.put(new Request(`https://${this.prefix}/${encodeURIComponent(e)}`),new Response(t,{headers:{"Content-Type":(()=>{switch(n.type){case"json":return"application/json";case"arrayBuffer":case"blob":return"application/octet-stream";default:return"text/plain"}})()}})).then(r(!0)).catch((e=>{r(!1)}))}))}))},this.delete=async function(e){return new Promise(((t,n)=>{caches.open(this.namespace).then((n=>{n.delete(new Request(`https://${this.prefix}/${encodeURIComponent(e)}`)).then(t(!0)).catch((e=>{t(!1)}))}))}))}}return e}()},710:(__unused_webpack___webpack_module__,__webpack_exports__,__webpack_require__)=>{"use strict";__webpack_require__.d(__webpack_exports__,{Z:()=>__WEBPACK_DEFAULT_EXPORT__});var js_yaml__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(272),_chenyfan_cache_db__WEBPACK_IMPORTED_MODULE_1__=__webpack_require__(385),_utils_engine_js__WEBPACK_IMPORTED_MODULE_2__=__webpack_require__(671),_package_json__WEBPACK_IMPORTED_MODULE_3__=__webpack_require__(147);const router_cgi=async request=>{const db=new _chenyfan_cache_db__WEBPACK_IMPORTED_MODULE_1__,urlStr=request.url.toString(),urlObj=new URL(urlStr),pathname=urlObj.pathname,q=e=>urlObj.searchParams.get(e);let config;switch(pathname.split("/")[2]){case"hello":return new Response("Hello ClientWorker!");case"info":return new Response(JSON.stringify({version:_package_json__WEBPACK_IMPORTED_MODULE_3__.i8}),{headers:{"Content-Type":"application/json"}});case"page":return"install"===q("type")?fetch("/404"):new Response("Error, page type not found");case"api":switch(q("type")){case"config":return fetch(q("url")||"/config.yaml").then((e=>e.text())).then((e=>js_yaml__WEBPACK_IMPORTED_MODULE_0__.ZP.load(e))).then((async e=>(await db.write("config",JSON.stringify(e),{type:"json"}),new Response("ok")))).catch((async e=>(await db.write("config",""),new Response(e))));case"clear":return caches.open("ClientWorker_ResponseCache").then((async e=>e.keys().then((async t=>(await Promise.all(t.map((t=>{e.delete(t)}))),new Response("ok"))))));case"hotpatch":if(config=JSON.parse(await db.read("config")),"object"!=typeof config.hotpatch)return new Response("Error, config.hotpatch not found");const hotpatch=config.hotpatch;return await _utils_engine_js__WEBPACK_IMPORTED_MODULE_2__.Z.parallel(hotpatch).then((e=>e.text())).then((async script=>{await db.write("hotpatch",script,{type:"text"}),eval(script)})),new Response("ok");case"hotconfig":if(config=JSON.parse(await db.read("config")),"object"!=typeof config.hotconfig)return new Response("Error, config.hotconfig not found");const hotconfig=config.hotconfig,nConfig=await _utils_engine_js__WEBPACK_IMPORTED_MODULE_2__.Z.parallel(hotconfig).then((e=>e.text())).then((e=>js_yaml__WEBPACK_IMPORTED_MODULE_0__.ZP.load(e))).then((e=>JSON.stringify(e))).catch((e=>""));return nConfig&&await db.write("config",nConfig),new Response("ok");default:return new Response("Error, api type not found")}default:return new Response("Not Found!, Client Worker!")}},__WEBPACK_DEFAULT_EXPORT__=router_cgi},755:(__unused_webpack___webpack_module__,__unused_webpack___webpack_exports__,__webpack_require__)=>{"use strict";var _cgi_js__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(710),_chenyfan_cache_db__WEBPACK_IMPORTED_MODULE_1__=__webpack_require__(385),_utils_cons_js__WEBPACK_IMPORTED_MODULE_2__=__webpack_require__(70),_utils_engine_js__WEBPACK_IMPORTED_MODULE_3__=__webpack_require__(671),_utils_rebuild_js__WEBPACK_IMPORTED_MODULE_4__=__webpack_require__(431);self.clientworkerhandle=async request=>{const domain=new URL(new Request("").url).host,db=new _chenyfan_cache_db__WEBPACK_IMPORTED_MODULE_1__;let tReq=request;const urlStr=tReq.url.toString(),urlObj=new URL(urlStr),pathname=urlObj.pathname;if("cw-cgi"===pathname.split("/")[1])return(0,_cgi_js__WEBPACK_IMPORTED_MODULE_0__.Z)(request);const config=await db.read("config",{type:"json"});if(!config)return fetch(request);let tFetched=!1,EngineFetch=!1,fetchConfig={},EngineFetchList=[],tRes=new Response;for(let catch_rule of config.catch_rules)if("_"===catch_rule.rule&&(catch_rule.rule=domain),tReq.url.match(new RegExp(catch_rule.rule)))for(let transform_rule of catch_rule.transform_rules){let tSearched=!1;switch("_"===transform_rule.search&&(transform_rule.search=catch_rule.rule),transform_rule.searchin||"url"){case"url":tReq.url.match(new RegExp(transform_rule.search,transform_rule.searchflags))&&(tSearched=!0);break;case"header":tReq.headers.get(transform_rule.searchkey).match(new RegExp(transform_rule.search,transform_rule.searchflags))&&(tSearched=!0);break;case"status":if(!tFetched){_utils_cons_js__WEBPACK_IMPORTED_MODULE_2__.Z.w(`${tReq.url} is not fetched yet,the status rule are ignored`);break}String(tRes.status).match(new RegExp(transform_rule.search,transform_rule.searchflags))&&(tSearched=!0);break;case"statusText":if(!tFetched){_utils_cons_js__WEBPACK_IMPORTED_MODULE_2__.Z.w(`${tReq.url} is not fetched yet,the statusText rule are ignored`);break}tRes.statusText.match(new RegExp(transform_rule.search,transform_rule.searchflags))&&(tSearched=!0);break;case"body":if(!tFetched){_utils_cons_js__WEBPACK_IMPORTED_MODULE_2__.Z.w(`${tReq.url} is not fetched yet,the body rule are ignored`);break}(await tRes.clone().text()).match(new RegExp(transform_rule.search,transform_rule.searchflags))&&(tSearched=!0);break;default:_utils_cons_js__WEBPACK_IMPORTED_MODULE_2__.Z.e(`${tReq.url} the ${transform_rule.searchin} search rule are not supported`)}switch(transform_rule.replacein||"url"){case"url":if(tFetched&&tSearched){_utils_cons_js__WEBPACK_IMPORTED_MODULE_2__.Z.w(`${tReq.url} is already fetched,the url transform rule:${transform_rule.search} are ignored`);break}if(void 0!==transform_rule.replace&&tSearched)if("string"==typeof transform_rule.replace)EngineFetch&&_utils_cons_js__WEBPACK_IMPORTED_MODULE_2__.Z.w(`EngineFetch Disabled for ${tReq.url},the request will downgrade to normal fetch`),tReq=_utils_rebuild_js__WEBPACK_IMPORTED_MODULE_4__.Z.request(tReq,{url:tReq.url.replace(new RegExp(transform_rule.replacekey||transform_rule.search,transform_rule.replaceflags),transform_rule.replace)}),EngineFetch=!1;else{if(EngineFetch){_utils_cons_js__WEBPACK_IMPORTED_MODULE_2__.Z.w(`Replacement cannot be used for ${tReq.url},the request is already powered by fetch-engine `);break}transform_rule.replace.forEach((e=>{"_"!==e?EngineFetchList.push(_utils_rebuild_js__WEBPACK_IMPORTED_MODULE_4__.Z.request(tReq,{url:tReq.url.replace(new RegExp(transform_rule.replacekey||transform_rule.search,transform_rule.replaceflags),e)})):EngineFetchList.push(tReq)})),EngineFetch=!0}break;case"body":tSearched&&(tFetched?tRes=_utils_rebuild_js__WEBPACK_IMPORTED_MODULE_4__.Z.response(tRes,{body:(await tRes.clone().text()).replace(new RegExp(transform_rule.replacekey||transform_rule.search,transform_rule.replaceflags),transform_rule.replace)}):tReq=_utils_rebuild_js__WEBPACK_IMPORTED_MODULE_4__.Z.request(tReq,{body:(await tReq.clone().text()).replace(new RegExp(transform_rule.replacekey||transform_rule.search,transform_rule.replaceflags),transform_rule.replace)}));break;case"status":"string"==typeof transform_rule.replace&&tSearched&&(tRes=_utils_rebuild_js__WEBPACK_IMPORTED_MODULE_4__.Z.response(tRes,{status:tRes.status.replace(new RegExp(transform_rule.replacekey||transform_rule.search,transform_rule.replaceflags),transform_rule.replace)}));break;case"statusText":"string"==typeof transform_rule.replace&&tSearched&&(tRes=_utils_rebuild_js__WEBPACK_IMPORTED_MODULE_4__.Z.response(tRes,{statusText:tRes.statusText.replace(new RegExp(transform_rule.replacekey||transform_rule.search,transform_rule.replaceflags),transform_rule.replace)}));break;default:_utils_cons_js__WEBPACK_IMPORTED_MODULE_2__.Z.e(`${tReq.url} the ${transform_rule.replacein} replace rule are not supported`)}if(tSearched){if("object"==typeof transform_rule.header)for(var header in transform_rule.header)tFetched?tRes=_utils_rebuild_js__WEBPACK_IMPORTED_MODULE_4__.Z.response(tRes,{headers:{[header]:transform_rule.header[header]}}):tReq=_utils_rebuild_js__WEBPACK_IMPORTED_MODULE_4__.Z.request(tReq,{headers:{[header]:transform_rule.header[header]}});if(void 0!==transform_rule.action)switch(transform_rule.action){case"skip":return fetch(request);case"fetch":if(tFetched){_utils_cons_js__WEBPACK_IMPORTED_MODULE_2__.Z.w(`${tReq.url} is already fetched,the fetch action are ignored`);break}if(void 0===transform_rule.fetch){_utils_cons_js__WEBPACK_IMPORTED_MODULE_2__.Z.e(`Fetch Config is not defined for ${tReq.url}`);break}if(fetchConfig={status:transform_rule.fetch.status,mode:transform_rule.fetch.mode,credentials:transform_rule.fetch.credentials,redirect:transform_rule.fetch.redirect,timeout:transform_rule.fetch.timeout,threads:transform_rule.fetch.threads,limit:transform_rule.fetch.limit},!transform_rule.fetch.preflight)for(var eReq in tReq=new Request(tReq.url,{method:(method=tReq.method,"GET"===method||"HEAD"===method||"POST"===method?method:"GET"),body:(body=tReq.body,"POST"===tReq.method?body:null)}),delete fetchConfig.credentials,EngineFetchList)EngineFetchList[eReq]=new Request(EngineFetchList[eReq].url,tReq);tRes=await new Promise((async(res,rej)=>{const EngineFetcher=async()=>{let cRes;return new Promise((async(resolve,reject)=>{if(EngineFetch)switch(transform_rule.fetch.engine||"parallel"){case"classic":cRes=await _utils_engine_js__WEBPACK_IMPORTED_MODULE_3__.Z.classic(EngineFetchList,fetchConfig);break;case"parallel":cRes=await _utils_engine_js__WEBPACK_IMPORTED_MODULE_3__.Z.parallel(EngineFetchList,fetchConfig);break;case"KFCThursdayVW50":4===(new Date).getDay()&&_utils_cons_js__WEBPACK_IMPORTED_MODULE_2__.Z.e("VW50! The Best Fetch Engine in the World Said!"),cRes=await _utils_engine_js__WEBPACK_IMPORTED_MODULE_3__.Z.KFCThursdayVW50(EngineFetchList,fetchConfig);break;default:_utils_cons_js__WEBPACK_IMPORTED_MODULE_2__.Z.e(`Fetch Engine ${transform_rule.fetch.engine} is not supported`)}else switch(transform_rule.fetch.engine||"fetch"){case"fetch":cRes=await _utils_engine_js__WEBPACK_IMPORTED_MODULE_3__.Z.fetch(tReq,fetchConfig);break;case"crazy":cRes=await _utils_engine_js__WEBPACK_IMPORTED_MODULE_3__.Z.crazy(tReq,fetchConfig);break;default:_utils_cons_js__WEBPACK_IMPORTED_MODULE_2__.Z.e(`${tReq.url} the ${transform_rule.fetch.engine} engine are not supported`)}"object"==typeof transform_rule.fetch.cache&&cRes.status===(transform_rule.fetch.status||200)?(cRes=_utils_rebuild_js__WEBPACK_IMPORTED_MODULE_4__.Z.response(cRes,{headers:{ClientWorker_ExpireTime:(new Date).getTime()+Number(eval(transform_rule.fetch.cache.expire||"0"))}}),caches.open("ClientWorker_ResponseCache").then((e=>{e.put(tReq,cRes.clone()).then((()=>{resolve(cRes)}))}))):resolve(cRes)}))};"object"==typeof transform_rule.fetch.cache?caches.open("ClientWorker_ResponseCache").then((e=>{e.match(tReq).then((e=>{if(e){if(Number(e.headers.get("ClientWorker_ExpireTime"))>(new Date).getTime())return _utils_cons_js__WEBPACK_IMPORTED_MODULE_2__.Z.s(`${tReq.url} is fetched from cache`),void res(e);_utils_cons_js__WEBPACK_IMPORTED_MODULE_2__.Z.w(`${tReq.url} is expired.`),res(Promise.any([EngineFetcher(),new Promise((async(t,n)=>{setTimeout((()=>{_utils_cons_js__WEBPACK_IMPORTED_MODULE_2__.Z.e(`${tReq.url} is too late to fetch,even though the cache has expired,so return by cache`),t(e)}),transform_rule.fetch.cache.delay||3e3)}))]))}else _utils_cons_js__WEBPACK_IMPORTED_MODULE_2__.Z.w(`${tReq.url} is not cached!And it is too late to fetch!`),res(EngineFetcher())}))})):res(EngineFetcher())})),tFetched=!0;break;case"redirect":if(void 0===transform_rule.redirect){_utils_cons_js__WEBPACK_IMPORTED_MODULE_2__.Z.e(`Redirect Config is not defined for ${tReq.url}`);break}return"string"==typeof transform_rule.redirect.url?Response.redirect(transform_rule.redirect.url,transform_rule.redirect.status||301):Response.redirect(tReq.url.replace(new RegExp(transform_rule.search),transform_rule.redirect.to),transform_rule.redirect.status||301);case"return":return void 0===transform_rule.return&&(transform_rule.return={}),new Response(transform_rule.return.body||"Error!",{status:transform_rule.return.status||503,headers:transform_rule.return.headers||{}});case"script":if(void 0===transform_rule.script){_utils_cons_js__WEBPACK_IMPORTED_MODULE_2__.Z.e(`Script Config is not defined for ${tReq.url}`);break}if("string"==typeof transform_rule.script.function){const ClientWorkerAnonymousFunctionName=`ClientWorker_AnonymousFunction_${(new Date).getTime()}`;self[ClientWorkerAnonymousFunctionName]=eval(transform_rule.script.function),transform_rule.script.name=ClientWorkerAnonymousFunctionName}const ScriptAns=await Function("return ("+transform_rule.script.name+")")()({fetched:tFetched,request:tReq,response:tRes});if(ScriptAns.fetched){if(transform_rule.script.skip)return ScriptAns.response;tFetched=!0,tRes=ScriptAns.response}else tReq=ScriptAns.request;break;default:_utils_cons_js__WEBPACK_IMPORTED_MODULE_2__.Z.w(`This Action:${transform_rule.action} is not supported yet`)}}}var body,method;return tFetched||(new URL(tReq.url).host!==domain&&(tRes=await fetch(tReq)),tRes=EngineFetch?await _utils_engine_js__WEBPACK_IMPORTED_MODULE_3__.Z.parallel(EngineFetchList,fetchConfig||{}):await fetch(tReq.url,{method:tReq.method.match(/^(GET|HEAD|POST)$/g)?tReq.method:"GET",body:tReq.method.match(/^(POST)$/g)?tReq.body:void 0})),tRes};var __WEBPACK_DEFAULT_EXPORT__={}},70:(e,t,n)=>{"use strict";n.d(t,{Z:()=>r});const r={s:e=>{console.log(`%c[SUCCESS]%c ${e}`,"color:white;background:green;","")},w:e=>{console.log(`%c[WARNING]%c ${e}`,"color:brown;background:yellow;","")},i:e=>{console.log(`%c[INFO]%c ${e}`,"color:white;background:blue;","")},e:e=>{console.log(`%c[ERROR]%c ${e}`,"color:white;background:red;","")},d:e=>{console.log(`%c[DEBUG]%c ${e}`,"color:white;background:black;","")}}},671:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var r=n(70),i=n(431);Promise.any||(Promise.any=function(e){return new Promise(((t,n)=>{let r=(e=Array.isArray(e)?e:[]).length,i=[];if(0===r)return n(new AggregateError("All promises were rejected"));e.forEach((e=>{e.then((e=>{t(e)}),(e=>{r--,i.push(e),0===r&&n(new AggregateError(i))}))}))}))});const a={fetch:async(e,t)=>(t=t||{status:200},new Promise(((n,r)=>{const i=Object.prototype.toString.call(e);"[object String]"!==i&&"[object Request]"!==i&&r(`FetchEngine.fetch: req must be a string or Request object,but got ${i}`),setTimeout((()=>{r(new Response("504 All GateWays Failed,ClientWorker Show This Page,Engine Fetch",{status:504,statusText:"504 All Gateways Timeout"}))}),t.timeout||5e3),fetch(e,{mode:t.mode,credentials:t.credential,redirect:t.redirect||"follow"}).then((e=>{n(e)})).catch((e=>{r(e)}))}))),crazy:async(e,t)=>{(t=t||{status:200}).threads=t.threads||4,t.trylimit=t.trylimit||10;const n=Object.prototype.toString.call(e);if("[object String]"!==n&&"[object Request]"!==n)return void r.Z.e(`FetchEngine.fetch: req must be a string or Request object,but got ${n}`);const o=new AbortController,s=await fetch(e,{signal:o.signal,mode:t.mode,credentials:t.credential,redirect:t.redirect||"follow"}),c=s.headers,l=c.get("Content-Length");return s.status!==(t.status||200)?new Response("504 All GateWays Failed,ClientWorker Show This Page,Engine Crazy",{status:504,statusText:"504 All Gateways Timeout"}):(o.abort(),!l||l<t.threads?(r.Z.e(`FetchEngine.crazy: The Origin is not support Crazy Mode,or the size of the file is less than ${t.threads} bytes,downgrade to normal fetch`),a.fetch(e,t)):new Promise(((n,r)=>{const a=parseInt(l/t.threads),o=[];for(let n=0;n<t.threads;n++)o.push(new Promise((async(o,s)=>{let c=1;const l=async()=>{c+=1;const o=i.Z.request(e,{headers:{Range:`bytes=${n*a}-${(n+1)*a-1}`},url:e.url});return fetch(o,{mode:t.mode,credentials:t.credential,redirect:t.redirect||"follow"}).then((e=>e.arrayBuffer())).catch((e=>{if(!(c>=t.trylimit))return l();r()}))};o(l())})));Promise.all(o).then((e=>{const t=[];for(let n=0;n<e.length;n++)t.push(e[n]);n(new Response(new Blob(t),{headers:c,status:200,statusText:"OK"}))})),setTimeout((()=>{r(new Response("504 All GateWays Failed,ClientWorker Show This Page,Engine Crazy",{status:504,statusText:"504 All Gateways Timeout"}))}),t.timeout||5e3)})))},KFCThursdayVW50:async(e,t)=>{(t=t||{status:200}).threads=t.threads||4,t.trylimit=t.trylimit||10;const n=Object.prototype.toString.call(e);if("[object String]"===n||"[object Request]"===n)return r.Z.w("FetchEngine.KFCThursdayVW50: reqs is a string or Request object,downgrade to crazy"),a.crazy(e,t);if("[object Array]"!==n)return r.Z.e(`FetchEngine.KFCThursdayVW50: reqs must be a string or Request object or an array,but got ${n}`),Promise.reject(`FetchEngine.KFCThursdayVW50: reqs must be a string or Request object or an array,but got ${n}`);if("[object Array]"===n&&(0===e.length&&(r.Z.e("FetchEngine.KFCThursdayVW50: reqs array is empty"),reject()),1===e.length))return r.Z.w("FetchEngine.KFCThursdayVW50: reqs array is only one,downgrade to crazy"),a.crazy(e[0],t);const o=new AbortController,s=await a.parallel(e,{signal:o.signal,mode:t.mode,credentials:t.credential,redirect:t.redirect||"follow",timeout:t.timeout||3e4}),c=s.headers,l=c.get("Content-Length");return s.status!==(t.status||200)&&reject(new Response("504 All GateWays Failed,ClientWorker Show This Page,Engine KFCThursdayVW50",{status:504,statusText:"504 All Gateways Timeout"})),o.abort(),!l||l<t.threads?(r.Z.e(`FetchEngine.KFCThursdayVW50: The Origin is not support KFCThursdayVW50 Mode,or the size of the file is less than ${t.threads} bytes,downgrade to normal fetch`),a.fetch(e,t)):new Promise(((n,o)=>{const s=parseInt(l/t.threads),u=[];for(let n=0;n<t.threads;n++)u.push(new Promise((async(c,l)=>{let u=1;const p=async()=>{u+=1;const c=[];return e.forEach((e=>{c.push(i.Z.request(e,{headers:{Range:`bytes=${n*s}-${(n+1)*s-1}`},url:e.url}))})),a.parallel(c,{mode:t.mode,credentials:t.credential,redirect:t.redirect||"follow",timeout:t.timeout||3e4,status:206}).then((e=>e.arrayBuffer())).catch((async e=>{if(r.Z.e(`FetchEngine.KFCThursdayVW50: ${await e.text()}`),!(u>=t.trylimit))return p();o()}))};c(p())})));Promise.all(u).then((e=>{const t=[];for(let n=0;n<e.length;n++)t.push(e[n]);n(new Response(new Blob(t),{headers:c,status:200,statusText:"OK"}))})),setTimeout((()=>{o(new Response("504 All GateWays Failed,ClientWorker Show This Page,Engine KFCThursdayVW50",{status:504,statusText:"504 All Gateways Timeout"}))}),t.timeout||3e4)}))},classic:async(e,t)=>new Promise(((n,i)=>{t=t||{status:200};const o=Object.prototype.toString.call(e);"[object String]"===o||"[object Request]"===o?(r.Z.w(`FetchEngine.classic: reqs should be an array,but got ${o},this request will downgrade to normal fetch`),n(a.fetch(e,t))):"[object Array]"!==o?(r.Z.e(`FetchEngine.classic: reqs must be a string , Request or Array object,but got ${o}`),i()):"[object Array]"===o&&(0===o.length&&(r.Z.e("FetchEngine.classic: reqs array is empty"),i()),1===o.length&&(r.Z.w("FetchEngine.classic: reqs array is only one element,this request will downgrade to normal fetch"),n(a.fetch(e[0],t))));const s=new AbortController,c=async e=>new Response(await e.arrayBuffer(),{status:e.status,headers:e.headers,statusText:e.statusText});Promise.any(e.map((e=>{fetch(e,{signal:s.signal,mode:t.mode,credentials:t.credential,redirect:t.redirect||"follow"}).then(c).then((e=>{e.status==(t.status||200)&&(s.abort(),n(e))})).catch((e=>{"DOMException: The user aborted a request."==e&&console.log()}))}))),setTimeout((()=>{i(new Response("504 All GateWays Failed,ClientWorker Show This Page,Engine Classic",{status:504,statusText:"504 All Gateways Timeout"}))}),t.timeout||5e3)})),parallel:async(e,t)=>new Promise(((n,o)=>{t=t||{status:200};const s=Object.prototype.toString.call(e);"[object String]"===s||"[object Request]"===s?(r.Z.w(`FetchEngine.parallel: reqs should be an array,but got ${s},this request will downgrade to normal fetch`),n(a.fetch(e,t))):"[object Array]"!==s?(r.Z.e(`FetchEngine.parallel: reqs must be a string , Request or Array object,but got ${s}`),o()):"[object Array]"===s&&(0===s.length&&(r.Z.e("FetchEngine.parallel: reqs array is empty"),o()),1===s.length&&(r.Z.w("FetchEngine.parallel: reqs array is only one element,this request will downgrade to normal fetch"),n(a.fetch(e[0],t))));const c=new Event("abortOtherInstance"),l=new EventTarget;Promise.any(e.map((async e=>{let r=new AbortController,a=!1;l.addEventListener(c.type,(()=>{a||r.abort()})),fetch(e,{signal:r.signal,mode:t.mode,credentials:t.credential,redirect:t.redirect||"follow"}).then((e=>{e.status==(t.status||200)&&(a=!0,l.dispatchEvent(c),n(i.Z.response(e,{})))})).catch((e=>{"DOMException: The user aborted a request."==e&&console.log()}))}))),setTimeout((()=>{o(new Response("504 All GateWays Failed,ClientWorker Show This Page,Engine Parallel",{status:504,statusText:"504 All Gateways Timeout"}))}),t.timeout||5e3)}))},o=a},431:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var r=n(70);const i={request:(e,t)=>{"navigate"===(e=e.clone()).mode&&r.Z.w("You can't rebuild a POST method with body when it is a navigate request.ClientWorker will ignore it's body");let n=new Request(e,{headers:a(e,t.headers),method:t.method||e.method,mode:"navigate"===e.mode?"same-origin":t.mode||e.mode,credentials:t.credentials||e.credentials,redirect:t.redirect||e.redirect});return t.url&&(n=new Request(t.url,n)),n},response:(e,t)=>{if("opaque"===e.type)return r.Z.e("You can't rebuild a opaque response.ClientWorker will ignore this build"),e;return new Response(e.body,{headers:a(e,t.headers),status:t.status||e.status,statusText:t.statusText||e.statusText})}},a=(e,t)=>{if(t){const n=new Headers(e.headers);for(let e in t)void 0!==t[e]?n.set(e,t[e]):n.delete(e);return n}return new Headers(e.headers)},o=i},272:(e,t,n)=>{"use strict"; |
写入配置
放置配置文件
- 在
source
下创建一个名为config.yaml
的文件 - 在
主目录下的config.yml
文件进行配置
1 | skip_render: |
原始配置
1 | name: ClientWorker |
部分功能配置
NPM
1 | - rule: ^https\:\/\/((cdn|fastly|gcore|test1|quantil)\.jsdelivr\.net\/npm|unpkg\.com) |
cdnjs
1 | - rule: ^https\:\/\/cdnjs\.cloudflare\.com\/ajax\/libs |
GitHub
1 | - rule: ^https\:\/\/(cdn|fastly|gcore|test1|quantil)\.jsdelivr\.net\/gh |
放置安装文件
文件地址
随便放不管放在哪,引入
最重要(越靠前越好)
1 | inject: |
类型介绍
共有三种方式接入: 三文件全域安装 、 自定义无刷新安装 、 自定义刷新安装
其中,全域安装最简单,对 SEO 支持也最恶劣(Google 会提示额外的计算开销,而百度完全没办法爬取)。比较适用于自用的、只追求速度的。自定义无刷新安装则对你的 HTML 和 JS 水平有所要求,对于部分不遵守标准的浏览器兼容性较差,但是这种方法对 SEO 没有影响,比较适合于对 seo 注重的网站。自定义刷新安装对 seo 略有影响,会在载入后阻断未经 CW 的请求并刷新一次,以便于 CW 及时托管,比较适合于网站提速
自定义无刷新安装
这种方式有一个重载的动作,即在无刷新的情况下将当前页面重新获取并填充。这可能会出现意外的兼容性错误,请慎行。 如果你不需要重载,请将下方重载标识框内的代码删除。不重载的后果就是用户首屏的大部分请求无法被 CW 拦截。如果你希望用户首屏进入就被托管,请使用’自定义刷新安装’
1 | if (!!navigator.serviceWorker) { |
自定义刷新安装
1 | <script> |
结尾
剩下功能,请前往官网查看@传送门
评论
匿名评论隐私政策
✅ 你无需删除空行,直接评论以获取最佳展示效果