【JS】如何处理JS脚本加载失败

前言

单页面应用基于JS构建,如果某个JS加载失败可能导致页面无法显示。
所以需要对加载失败时进行处理,首先需要捕获失败时机,其次尝试进行重新加载

捕获错误

可以通过 scriptonerror 捕获,但是不适用于框架中,手动写太过繁琐。

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <script onerror="console.log('出错了'); " src=" http://static.com/1.js"></script>
  <script onerror="console.log('出错了'); " src=" http://other-domain.com/2.js"></script>
  <script onerror="console.log('出错了'); " src=" http://xxx.com/3.js"></script>
</body>

</html>

可以给 window 添加 error事件,但需要注意的是,必须确保在加载JS前监听error事件,而且因为 error事件 不参与冒泡,需要在捕获阶段拿到 error,即第三个参数为true

<script>
  window.addEventListener('error', (e) => {
    console.log(e);
  },true)
</script>

确保捕获到的是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>
    window.addEventListener('error', (e) => {
      console.log(e);
      const tag = e.target
      if (tag.tagName === "SCRIPT" && !(e instanceof ErrorEvent)) {
        console.log("JS加载失败");
      }
    }, true)
  </script>
</head>

<body>
  <script src=" http://static.com/1.js"></script>
  <script src=" http://other-domain.com/2.js"></script>
  <script src=" http://xxx.com/3.js"></script>
  <script>
    throw 123
  </script>
</body>

</html>

重试加载

<!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 = ['xxx.com', 'xxx1.com', 'xxx2.com']
    const maxRetry = 3
    const retryInfo = {}

    window.addEventListener('error', (e) => {
      const tag = e.target
      if (tag.tagName === "SCRIPT" && !(e instanceof ErrorEvent)) {
        const url = new URL(tag.src)
        // 如果首次加载失败,则重试
        if (!retryInfo[url.pathname]) {
          retryInfo[url.pathname] = { times: 0, nextIndex: 0 }
        }
        const info = retryInfo[url.pathname]

        if (info.times >= maxRetry) {
          console.log(url.pathname + "重试次数已达上限");
          return
        }

        const script = document.createElement('script')
        url.host = domains[info.nextIndex]
        script.src = url.toString()
        document.body.insertBefore(script, tag)
        info.times++
        info.nextIndex = (info.nextIndex + 1) % domains.length

      }
    }, true)
  </script>
</head>

<body>
  <script src=" http://static.com/1.js"></script>
  <script src=" http://other-domain.com/2.js"></script>
  <script>
    throw 123
  </script>
</body>

</html>

但是无法保证插入的script执行顺序,需要在每次插入script后阻塞代码,使用document.write即可,注意:由于逻辑是在script标签中,所以需要转义,否则编译器可能会误认为script标签结束了。

<!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 = ['xxx.com', 'xxx1.com', 'xxx2.com']
    const maxRetry = 3
    const retryInfo = {}

    window.addEventListener('error', (e) => {
      const tag = e.target
      if (tag.tagName === "SCRIPT" && !(e instanceof ErrorEvent)) {
        const url = new URL(tag.src)
        // 如果首次加载失败,则重试
        if (!retryInfo[url.pathname]) {
          retryInfo[url.pathname] = { times: 0, nextIndex: 0 }
        }
        const info = retryInfo[url.pathname]

        if (info.times >= maxRetry) {
          console.log(url.pathname + "重试次数已达上限");
          return
        }

        const script = document.createElement('script')
        url.host = domains[info.nextIndex]
        document.write(`<script src="${url.toString()}">\<\/script>`)
        info.times++
        info.nextIndex = (info.nextIndex + 1) % domains.length

      }
    }, true)
  </script>
</head>

<body>
  <script src=" http://static.com/1.js"></script>
  <script src=" http://other-domain.com/2.js"></script>
  <script>
    throw 123
  </script>
</body>

</html>
  • 15
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

田本初

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值