面试官:你的项目有什么亮点?我:解决了JS脚本加载失败的问题!

news2024/10/7 18:31:36

前后端面试题库 (面试必备) 推荐:★★★★★

地址:前端面试题库  web前端面试题库 VS java后端面试题库大全

面试官:你的项目有什么亮点?解决了什么问题?
你:嗯......
面试官:回去等通知吧。

上面的对话真可谓是面试环节最令人揪心的场景了,如果你想要从容面对面试时的项目提问环节,那么你一定要看完今天这篇文章。

我是渡一子辰老师,今天带你解决js脚本加载失败的问题。

JS 加载失败的危害

我们都知道,现代的网页离不开 JS,它可以让页面变得更加动态和交互。

但是,JS 也有可能加载失败,导致页面样式错乱,甚至白屏无法使用。

这对用户体验是非常不利的,尤其是对于单页应用,如果 JS 加载不出来,用户就无法继续浏览页面了。

那么,JS 加载失败的原因有哪些呢?

可能是网络不稳定,可能是服务器出错,可能是跨域问题,也可能是其他未知的因素。

我们无法控制这些原因,但我们可以通过一个简单的方法来解决加载失败的问题:重试!

JS 加载失败的解决方案

重试就是当 JS 加载失败时,重新请求一次或多次,直到成功为止。

这样可以增加加载成功的概率,也可以避免用户看到错误的页面。

那么,怎么实现重试呢?其实只需要解决两个问题:

  1. 什么时候重试?
  2. 如何重试?

什么时候重试?

要知道什么时候重试,我们就要知道什么时候 JS 加载失败。

最简单的就是给 script 标签,加一个 onerror 事件。当出现错误的时候 script 会触发这个事件。
为了方便测试,我们本地有三个 JS,名字分别为 1、2、3,分别输出 1、2、3。

<script onerror="console.log(123)" src="http://127.0.0.1:5500/js/1.js"></script>
<script onerror="console.log(123)" src="http://other-domain-one.com/js/2.js"></script>
<script onerror="console.log(123)" src="http://127.0.0.1:5500/js/3.js"></script>

这样做虽然可以,但不是最好的,会比较麻烦,又特别是在工程化的环境里边,这些 script 都是自动生成的,要加上 onerror 事件的话就会很复杂。

那么有没有更好的方法呢?当然有!我们可以利用事件委托的原理,在 window 上监听 error 事件,然后判断是否是 script 标签引起的错误。

注意:这里我们要在第三个参数传入 true,表示在捕获阶段触发事件,因为 error 事件不会冒泡。

<script src="http://127.0.0.1:5500/js/1.js"></script>
<script src="http://other-domain-one.com/js/2.js"></script>
<script src="http://127.0.0.1:5500/js/3.js"></script>
<script>
  window.addEventListener('error', (event) => {
    console.log('有错误!');
  }, true)
</script>

但是我们能这么写吗?同学们思考几秒钟。

其实是不行的,因为当前面的 JS 失败的时候,error 事件还没有注册,所以应该在最上方。

<script>
  window.addEventListener('error', (event) => {
    console.log('有错误!');
  }, true) 
</script>
<script src="http://127.0.0.1:5500/js/1.js"></script>
<script src="http://other-domain-one.com/js/2.js"></script>
<script src="http://127.0.0.1:5500/js/3.js"></script>

可以看到,我们已经触发 error 事件了。

但是这样还不够精确,因为 error 事件可能由其他原因引起,比如图片加载失败或者 JS 代码中抛出异常。

我们怎么区分呢?我们打印一下 error 的 event 值,看看它们有什么区别。

可以看到,图片和 script 引起的错误都是 Event 对象,而 JS 代码中抛出的错误是 ErrorEvent 对象。

并且 Event 对象中有一个 target 属性,指向触发错误的元素。

所以我们可以根据这两个特征来判断是否是 script 标签引起的错误。

<script>
  window.addEventListener('error', (event) => {
    // 拿到触发错误的标签
    const tag = event.target;
    // 便签的名称必须是 'SCRIPT' 与 event 错误的类型不能是 ErrorEvent
    if (tag.tagName === 'SCRIPT' && !(event instanceof ErrorEvent)) {
      console.log('script 加载错误');
    }
  }, true) 
</script>

这样我们就可以准确地捕获到 script 加载失败的情况了。

如何重试?

实现重试,我们就要重新创建一个 script 元素,并且修改它的 src 属性为一个新的域名。

为什么要修改域名呢?因为之前加载失败的域名可能已经失效了,所以我们需要准备一些备用域名,在加载失败时依次尝试。

那么我们需要记录以下三个信息:

  1. 备用域名列表
  2. 要重试的 script 的路径
  3. 已经重试过几次( 为了知道下一次要重试的备用域名是什么 )。

根据这些信息,我们可以写出以下代码:

<script>
  // 备用域名列表
  const domains = [
    'other-domain-two.com',
    'other-domain-three.com',
    'other-domain-four.com',
    '127.0.0.1:5500',
  ];
  // 重试的信息
  const retryInfo = {};
  window.addEventListener('error', (event) => {
    const tag = event.target;
    if (tag.tagName === 'SCRIPT' && !(event instanceof ErrorEvent)) {
      // 首先我们要知道是谁失败了,他请求的 js 是什么
      // 可以通过 url.pathnam 得到请求的 js 的名字
      const url = new URL(tag.src);
      // 我们判断一下重发的信息里有没有重试过这个 js
      if (!retryInfo[url.pathname]) {
        // 没重试过就给它添加一个
        retryInfo[url.pathname] = {
          times: 0, // 第几次重试从 0 开始
          nextIndex: 0, // 重试的域名也从 0 开始
        };
      }
      // 取出要重试的信息
      const info = retryInfo[url.pathname];
      // 这里我们要判断一下,重试的次数是否小于域名的列表长度,防止所有域名都失败时一直重复重试
      if (info.times < domains.length) {
        // 重试就要生成一个新的元素
        const script = document.createElement('script')
        // 那我们要重试呢就是替换一下失败的域名,所以可以利用 url.host,把要重试的域名替换它,
        url.host = domains[info.nextIndex]
        // 然后将新的 url 添加到新的 script 的 src 里
        script.src = url.toString()
        // 将新的 script 呢加入到失败的 script 之前
        document.body.insertBefore(script, tag)
        // 最后不要忘记重试信息的索引都要加 1
        info.times++
        info.nextIndex++;
      }
    }
  }, true) 
</script>

可以看到 2 已经输出了,但是顺序不对,应该是 1、2、3 的顺序,JS 的执行顺序是很重要的,因为他们之间可能有依赖关系,比如说 3 里有依赖 2 的东西,那么先加载 3 就会出现问题了。

出现这个问题的原因就在于新加入的这个元素没有阻塞后续的加载,也就是说我们创建的这个元素必须要它阻塞页面后续的加载。

这里就用到了一个同学们一定接触过,但是早就不使用的东西,同学思考一下,看能不能想到。

其实它叫做 document.write(),这个就会阻塞页面的加载。

<script>
  const domains = [
    'other-domain-two.com',
    'other-domain-three.com',
    '127.0.0.1:5500',
  ];
  const retryInfo = {};
  window.addEventListener('error', (event) => {
    const tag = event.target;
    if (tag.tagName === 'SCRIPT' && !(event instanceof ErrorEvent)) {
      const url = new URL(tag.src);
      if (!retryInfo[url.pathname]) {
        // 没重试过就给它添加一个
        retryInfo[url.pathname] = {
          times: 0, // 第几次重试从 0 开始
          nextIndex: 0, // 重试的域名也从 0 开始
        };
      }
      const info = retryInfo[url.pathname];
      if (info.times < domains.length) {
        const script = document.createElement('script')
        url.host = domains[info.nextIndex]
        // 阻塞页面后续的加载
        // 因为我们是写在 script 标签里 所以要转译一下,否则会被认为是 script 标签的结束
        document.write(`<script src="${url.toString()}"></script>`)
        info.times++
        info.nextIndex++;
      }
    }
  }, true) 
</script>

现在再看顺序就正常了,这里的警告是因为 document.write() 有阻塞,但是我们要的就是阻塞,所以就不用管他了。

总结

现在我们的问题已经解决了,但其实仍然可以再深入的去挖掘,比如 script 元素有 defer 怎么办?有 async 怎么办?这里就不展开叙述了。

前后端面试题库 (面试必备) 推荐:★★★★★

地址:前端面试题库  web前端面试题库 VS java后端面试题库大全

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/720310.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

7.3 【Linux】磁盘的分区、格式化、检验与挂载

想在系统中新增一颗磁盘时&#xff0c;需要做&#xff1a; 1.对磁盘进行分区&#xff0c;以创建可用的partition&#xff1b; 2.对该partition进行格式化&#xff08;format),以创建系统可用的filesystem&#xff1b; 3.可对刚刚创建好的filesystem进行检验&#xff1b; 4.…

Acrel-5000重点用能单位能耗在线监测系统在湖南三立集团的案例分析

安科瑞 崔丽洁 摘要&#xff1a;根据《重点用能节能办法》&#xff08;国家发展改革委等第七部委2018年15号令&#xff09;、《重点用能单位能耗在线监测系统推广建设工作方案》&#xff08;发改环资[2017]1711号&#xff09;和《关于加速推进重点用能单位能耗在线监测系统建设…

介绍几种OPTIONS检测的方法

概述 日常的VOIP开发中&#xff0c;OPTIONS检测是常用的网络状态检测工具。 OPTIONS原本是作为获取对方能力的消息&#xff0c;也可以检测当前服务状态。正常情况下&#xff0c;UAS收到OPTIONS心跳&#xff0c;直接回复200即可。 与ping不同的是&#xff0c;OPTIONS检测不仅…

前后端分离开发

目录 前后合开发&#xff08;不推荐&#xff09; 前后端分离开发&#xff08;主流&#xff09; 项目开发的基本步骤 接口文档的管理平台--YApi 前后合开发&#xff08;不推荐&#xff09; 沟通成本高分工不明确不便于管理不便于扩展 前后端分离开发&#xff08;主流&…

让浮动元素在一行显示

&#x1f4dd;个人主页&#xff1a;爱吃炫迈 &#x1f48c;系列专栏&#xff1a;HTMLCSS &#x1f9d1;‍&#x1f4bb;座右铭&#xff1a;道阻且长&#xff0c;行则将至&#x1f497; <div class"wrap"><div class"item">1</div><di…

ubuntu实现自动挂载u盘

ubuntu实现自动挂载u盘 但是&#xff0c;有些设施可以在没有图形工具的情况下进行复制&#xff0c;并且在系统上占用的空间非常小。 例如&#xff0c;在我的设置中&#xff0c;我已经实现了USB自动挂载服务&#xff0c;而无需使用任何外部工具/服务&#xff0c;只有udev和syst…

Junit5相关技术

Selenium自动化测试框架 Junit针对Java的单元测试框架 拿一个技术写自动化测试用例&#xff08;Selenium3&#xff09; 拿一个技术管理已经编写好的测试用例(Junit5) 写代码前需要添加依赖&#xff1a;Junit5 一、注解 1.1 Test 表示当前这个方法是一个测试用例 1.2 Di…

DCN v2阅读笔记

Deformable ConvNets v2: More Deformable, Better Results 是 Deformable Convolutional Networks 研究的续作&#xff0c;发表在 CVPR 2019上。 作者对 DCNv1 的自适应行为进行研究&#xff0c;观察到虽然其神经特征的空间支持比常规的卷积神经网络更符合物体结构&#xff0…

2023年的无线蓝牙耳机哪些牌子好,真无线蓝牙耳机品牌排名

本文将为您详细介绍每款蓝牙耳机的设计特点、音质表现、续航能力和智能功能等关键信息。我们将提供客观、全面的分析&#xff0c;帮助您更好地了解每款产品的优势和适用场景&#xff1b;无论您是追求高保真音质的音乐发烧友&#xff0c;还是需要轻便舒适的耳机进行运动&#xf…

Kafka入门, 消费者工作流程(十八)

kafka消费方式 pull(拉)模式&#xff1a; consumer采用从broker中主动拉取数据。 Kafka采用这种方式。 push(推)模式&#xff1a; Kafka没有采用这种方式&#xff0c;因为由broker决定消息发送速率&#xff0c;很难适应所有消费者的速率。例如推送速度是50m/s&#xff0c;consu…

rocketmq客户端日志过大造成磁盘使用率占用过高

目录 问题现象 排查占用 自定义客户端日志配置未生效 总结 问题现象 收到项目报警&#xff1a;磁盘占用率超标通知 排查占用 从上述可以看出&#xff0c;实质是跟正常业务日志无关的&#xff0c;/home/work/log挂出来了&#xff0c;与/根目录下无关 查看根目录下日志占用…

frp实现远程开机

前一篇刷完小米路由器后&#xff0c;自带了frp&#xff0c;所以继续走着 前提&#xff1a;(我有阿里云公网服务器、域名&#xff09; 第一步&#xff0c;配置路由器&#xff08;客户端&#xff09;frp [common] server_addr frp.xxx.cn #需要在阿里云服务器添加解析 serve…

【macOS 系列】如何取消程序坞下方app的未读消息的小红点

如何取消程序坞下方app的未读消息的小红点 需要针对单独的软件一次设置&#xff0c;不能批量设置。将标记为APP图标取消勾选即可。

【UnityDOTS 七】IAspect的使用

IAspect的使用 前言 IAspect是Unity DOTS中用来更方便组织数据集合的接口。可以将我们需要的数据集合放到一个IAspect中&#xff0c;然后用这个自定义的IAspect去进行查询。 一、IAspect是什么&#xff1f; 如上面官方文档说的&#xff0c;是一个高级的Component组合体。 可以…

Gartner发布2023新兴技术雷达图,超级自动化安全、生成式AI是高影响力技术

在Gartner发布的“2023新兴技术影响力雷达图”中&#xff0c;围绕智能世界、生产力革命、透明度和隐私****以及关键使能技术四大主题&#xff0c;对24种最具颠覆和市场变革性的新兴趋势和技术进行了评估。 其中&#xff0c;边缘AI、边缘计算机视觉处于雷达图核心位置&#xff…

Redis-问题

1.redis的槽是什么&#xff1f; Redis Cluster中有一个16384长度的槽的概念,他们的编号为0、1、2、3……16382、16383。这个槽是一个虚拟的槽,并不是真正存在的。 2.redis的槽有什么用&#xff1f; 缓存 对于高访问量的网站,缓存是提高性能的重要手段。使用Redis槽技术可以将…

机器学习23:《数据准备和特征工程-I》概述

机器学习帮助我们找到数据中的模式&#xff0c;然后我们用这些模式来预测新的数据点。为了获得正确的预测&#xff0c;我们必须构建数据集并正确地转换数据。在《数据准备和特征工程》系列文章中&#xff0c;笔者将重点介绍这两个关键步骤。在【机器学习7&#xff1a;特征工程】…

项目预算Budgeting 中英对照(PLM230 Unit4)

1、an estimate of the required funds 预估所需资金 2、correspond to the approved funds 符合批准的资金 3、availability control 可用性控制 4、be warned before the budget is exhausted 耗尽之前警告 5、The budget differs from the project cost plan is binding.…

为您的服务台提供6个基于AI的使用案例

人工智能&#xff08;AI&#xff09;正在向IT服务管理&#xff08;ITSM&#xff09;迈进&#xff0c;有望重新定义事物的工作方式。但是&#xff0c;人工智能是否会实现其承诺&#xff0c;并能够真正使ITSM更容易、更有效呢&#xff1f;这就是我们即将在此系列中所探讨的"…

计网笔记--网络层

1--网络层概述 网络层主要问题&#xff1a; ① 网络层向运输层提供怎样的服务&#xff1f; &#xff08;可靠或不可靠&#xff09; ② 网络层寻址问题&#xff08;IP 地址&#xff09; ③ 路由选择问题 &#xff08;路径&#xff09; 重点在于&#xff1a;TCP/IP协议栈 2--网络…