CDN加载失败的重试方案

1,032 阅读1分钟

cdn的加载有时候因为第三方的服务出问题,造成项目加载不了对应js,导致项目出问题。

解决方案:将错误的cdn地址替换为备用cdn地址。

项目结构

   demo
        --index.html
        --js/a.js
        --js/b.js

a.js

console.log("a.js");

b.js

console.log("b.js");
index.html 。此版本有js加载顺序的问题。
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script>
      const domains = ["cdn.jsdelivr.n", "cdn.jsdelivr.ne", "cdn.jsdelivr.net"];
      const retry = {};
      window.addEventListener(
        "error",
        (e) => {
          if (e.target.tagName !== "SCRIPT" || e instanceof ErrorEvent) {
            return;
          }
          const url = new URL(e.target.src);
          const name = url.pathname;
          if (!(name in retry)) {
            retry[name] = 0;
          }
          const index = retry[name];
          if (index >= domains.length) {
            return;
          }
          const newDomain = domains[index];
          const script = document.createElement("script");
          url.host = newDomain;
          script.src = url.toString();
          document.body.insertBefore(script, e.target);
          retry[name]++;
        },
        true
      );
    </script>
  </head>
  <body>
    <script src="./js/a.js"></script>
    <script src="https://xxxx/npm/echarts@5.4.3/dist/echarts.min.js"></script>
    <script src="./js/b.js"></script>
  </body>
</html>

image.png 期望的执行顺序应该是 a.js -> echarts.min.js -> c.js。但是以下的执行顺序是

  1. a.js 加完完毕
  2. echarts.min.js 报错重试,失败
  3. b.js 加完完毕
  4. echarts.min.js 再次报错重试,失败
  5. echarts.min.js 再次报错重试,成功 所以最后的执行顺序是 a.js -> b.js ->echarts.min.js image.png

同步执行最重要的是使用document.write。

解决方案如下

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script>
      const domains = ["cdn.jsdelivr.n", "cdn.jsdelivr.ne", "cdn.jsdelivr.net"];
      const retry = {};
      window.addEventListener(
        "error",
        (e) => {
          if (e.target.tagName !== "SCRIPT" || e instanceof ErrorEvent) {
            return;
          }
          const url = new URL(e.target.src);
          const name = url.pathname;
          if (!(name in retry)) {
            retry[name] = 0;
          }
          const index = retry[name];
          if (index >= domains.length) {
            return;
          }
          const newDomain = domains[index];
          url.host = newDomain;
          document.write(`\<script src="${url}">\<\/script>`);
          retry[name]++;
        },
        true
      );
    </script>
  </head>
  <body>
    <script src="./js/a.js"></script>
    <script src="https://xxxx/npm/echarts@5.4.3/dist/echarts.min.js"></script>
    <script src="./js/b.js"></script>
  </body>
</html>

image.png

  1. 期望的执行顺序应该是 a.js -> echarts.min.js -> c.js
  2. 实际的执行顺序应该是 a.js -> echarts.min.js -> c.js