前言

cdn.jsdelivr.net 国内无法访问时候,造成网站 js, css, image文件无法正常显示。
自动检查 cdn.jsdelivr.net 是否可用, 如果不可用时,会自动把所有 js, css, image 的地址切换到其他可用的域名。比如:gcore.jsdelivr.net

项目地址

本项目属于BestTools大佬的我只是转载。

使用方法

直接复制 index.js 或 index.min.js 里的内容,加到网站里。强烈建议添加到 head 标签最上面。
所有 script 标签加上 defer 属性。如果原来有 async 属性,可以跳过。这个可以避免 pending 状态带来的等待时间,大大提升性能。

示例:

<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); 

jsdelivr可用地址

  • fastly.jsdelivr.net
  • gcore.jsdelivr.net
  • testingcf.jsdelivr.net
  • test1.jsdelivr.net
Last modification:May 31, 2022
如果觉得我的文章对你有用,请随意赞赏