本文最后更新于 172 天前,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。
前言
cdn.jsdelivr.net
有时候会出现国内无法访问的情况,以至于造成网站 js
, css
, image
,字体
等文件无法正常显示。
因此 BestTools
大神开发出自动检查 cdn.jsdelivr.net
是否可用的脚本, 如果不可用时,会自动把所有资源地址切换到其他可用的域名。比如:gcore.jsdelivr.net
,fastly.jsdelivr.net
等其他 CDN
。
项目地址
Github 地址:https://github.com/PipecraftNet/jsdelivr-auto-fallback
使用方法
- 直接复制
index.js
或index.min.js
里的内容,加到网站里。强烈建议添加到head
标签最上面。 - 所有
script
标签加上defer
属性。如果原来有async
属性,可以跳过。这个可以避免pending
状态带来的等待时间,大大提升性能。 - 如果是
hexo
生成的网站,可以安装 hexo-filter-jsdelivr-auto-fallback 插件,自动添加。
-
示例:
# 可以把 js 文件放到其他目录下进行引用 <script defer src="index.js"></script> <script defer src="index.min.js"></script>
-
index.js
代码:((document) => { 'use strict'; let fastNode; let failed; let isRunning; const DEST_LIST = [ 'cdn.jsdelivr.net', 'fastly.jsdelivr.net', 'gcore.jsdelivr.net', 'cdn.zenless.top', 'testingcf.jsdelivr.net', 'test1.jsdelivr.net' ]; const PREFIX = '//'; const SOURCE = DEST_LIST[0]; const starTime = Date.now(); const TIMEOUT = 2000; const STORE_KEY = 'jsdelivr-auto-fallback'; const TEST_PATH = '/gh/PipecraftNet/jsdelivr-auto-fallback@main/empty.css?'; const shouldReplace = (text) => text && text.includes(PREFIX + SOURCE); const replace = (text) => text.replace(PREFIX + SOURCE, PREFIX + fastNode); const setTimeout = window.setTimeout; const $ = document.querySelectorAll.bind(document); const replaceElementSrc = () => { let element; let value; for (element of $('link[rel="stylesheet"]')) { value = element.href; if (shouldReplace(value) && !value.includes(TEST_PATH)) { element.href = replace(value); } } for (element of $('script')) { value = element.src; if (shouldReplace(value)) { const newNode = document.createElement('script'); newNode.src = replace(value); element.defer = true; element.src = ''; element.before(newNode); element.remove(); } } for (element of $('img')) { value = element.src; if (shouldReplace(value)) { // Used to cancel loading. Without this line it will remain pending status. element.src = ''; element.src = replace(value); } } // All elements that have a style attribute for (element of $('*[style]')) { value = element.getAttribute('style'); if (shouldReplace(value)) { element.setAttribute('style', replace(value)); } } for (element of $('style')) { value = element.innerHTML; if (shouldReplace(value)) { element.innerHTML = replace(value); } } }; const tryReplace = () => { if (!isRunning && failed && fastNode) { console.warn(SOURCE + ' is not available. Use ' + fastNode); isRunning = true; setTimeout(replaceElementSrc, 0); // Some need to wait for a while setTimeout(replaceElementSrc, 20); // Replace dynamically added elements setInterval(replaceElementSrc, 500); } }; const checkAvailable = (url, callback) => { let timeoutId; const newNode = document.createElement('link'); const handleResult = (isSuccess) => { if (!timeoutId) { return; } clearTimeout(timeoutId); timeoutId = 0; // Used to cancel loading. Without this line it will remain pending status. if (!isSuccess) newNode.href = 'data:text/plain;base64,'; newNode.remove(); callback(isSuccess); }; timeoutId = setTimeout(handleResult, TIMEOUT); newNode.addEventListener('error', () => handleResult(false)); newNode.addEventListener('load', () => handleResult(true)); newNode.rel = 'stylesheet'; newNode.text = 'text/css'; newNode.href = url + TEST_PATH + starTime; document.head.insertAdjacentElement('afterbegin', newNode); }; const cached = (() => { try { return Object.assign( {}, JSON.parse(localStorage.getItem(STORE_KEY) || '{}') ); } catch { return {}; } })(); const main = () => { cached.time = starTime; cached.failed = false; cached.fastNode = null; for (const url of DEST_LIST) { checkAvailable('https://' + url, (isAvailable) => { // console.log(url, Date.now() - starTime, Boolean(isAvailable)); if (!isAvailable && url === SOURCE) { failed = true; cached.failed = true; } if (isAvailable && !fastNode) { fastNode = url; } if (isAvailable && !cached.fastNode) { cached.fastNode = url; } tryReplace(); }); } setTimeout(() => { // If all domains are timeout if (failed && !fastNode) { fastNode = DEST_LIST[1]; tryReplace(); } localStorage.setItem(STORE_KEY, JSON.stringify(cached)); }, TIMEOUT + 100); }; if ( cached.time && starTime - cached.time < 60 * 60 * 1000 && cached.failed && cached.fastNode ) { failed = true; fastNode = cached.fastNode; tryReplace(); setTimeout(main, 1000); } else { main(); } })(document);
-
index.min.js
代码:(n=>{"use strict";let r,s,e;const l=["cdn.jsdelivr.net","fastly.jsdelivr.net","gcore.jsdelivr.net","cdn.zenless.top","testingcf.jsdelivr.net","test1.jsdelivr.net"],t="//",a=l[0],i=Date.now(),o=2e3,c="jsdelivr-auto-fallback",f="/gh/PipecraftNet/jsdelivr-auto-fallback@main/empty.css?",d=e=>e&&e.includes(t+a),m=e=>e.replace(t+a,t+r),u=window.setTimeout,v=n.querySelectorAll.bind(n),g=()=>{let e,t;for(e of v('link[rel="stylesheet"]'))t=e.href,d(t)&&!t.includes(f)&&(e.href=m(t));for(e of v("script"))if(t=e.src,d(t)){const r=n.createElement("script");r.src=m(t),e.defer=!0,e.src="",e.before(r),e.remove()}for(e of v("img"))t=e.src,d(t)&&(e.src="",e.src=m(t));for(e of v("*[style]"))t=e.getAttribute("style"),d(t)&&e.setAttribute("style",m(t));for(e of v("style"))t=e.innerHTML,d(t)&&(e.innerHTML=m(t))},y=()=>{!e&&s&&r&&(console.warn(a+" is not available. Use "+r),e=!0,u(g,0),u(g,20),setInterval(g,500))},b=(()=>{try{return Object.assign({},JSON.parse(localStorage.getItem(c)||"{}"))}catch{return{}}})();var h=()=>{b.time=i,b.failed=!1,b.fastNode=null;for(const t of l)((e,t)=>{let r;const s=n.createElement("link"),l=e=>{r&&(clearTimeout(r),r=0,e||(s.href="data:text/plain;base64,"),s.remove(),t(e))};r=u(l,o),s.addEventListener("error",()=>l(!1)),s.addEventListener("load",()=>l(!0)),s.rel="stylesheet",s.text="text/css",s.href=e+f+i,n.head.insertAdjacentElement("afterbegin",s)})("https://"+t,e=>{e||t!==a||(s=!0,b.failed=!0),e&&!r&&(r=t),e&&!b.fastNode&&(b.fastNode=t),y()});u(()=>{s&&!r&&(r=l[1],y()),localStorage.setItem(c,JSON.stringify(b))},o+100)};b.time&&i-b.time<36e5&&b.failed&&b.fastNode?(s=!0,r=b.fastNode,y(),u(h,1e3)):h()})(document);
用户脚本
作为用户,你也可以使用油猴脚本将网站中的 cdn.jsdelivr.net
替换为可以访问的域名。
- 浏览器安装
Tampermonkey
。 - 安装脚本: https://greasyfork.org/zh-CN/scripts/445701-jsdelivr-auto-fallback
jsdelivr 可用节点比较
gcore.jsdelivr.net | Gcore 节点 | 可用性高 |
testingcf.jsdelivr.net | Cloudflare 节点 | 可用性高 |
quantil.jsdelivr.net | Quantil 节点 | 可用性尚可 |
fastly.jsdelivr.net | Fastly 节点 | 可用性尚可 |
originfastly.jsdelivr.net | Fastly 节点 | 可用性低 |
cdn.jsdelivr.net | 通用节点 | 可用性低 |