JS 延迟加载方法

在这里插入图片描述

1 JS 延迟加载

『什么是 JS 延迟加载』

简单来说,JS 延迟加载就是等 HTML 文档解析完成后再执行 JS 脚本。


『JS 延迟加载有什么作用』

1️⃣ 有助于提高页面加载速度。因为外联普通 JavaScript 脚本的加载和执行都会阻塞 HTML 文档的解析,从而影响了页面加载速度。

2️⃣ 确保 JS 脚本能正确操作 DOM 对象。由于 JS 脚本执行时,HTML 的解析还未完全结束,可能会无法获取到对应的 DOM 对象,此时会返回 null

// index.js
const firstEl = document.getElementById('first');
const secodeEl = document.getElementById('secode');

console.log(firstEl);  // <div id="first"></div>
console.log(secodeEl);  // null
<body>
	<div id="first"></div>
	<script src="index.js"></script>
	<div id="secode"></div>
</body>

『JS 延迟加载有哪些方式』

  • defer 属性
  • async 属性
  • 动态创建 DOM 方式
  • 使用 setTImeout 延迟方法
  • 让 JS 最后加载

为了更好了解各个方式的 JS 脚本执行时机,先看一下外联普通 JavaScript 执行过程:

// 普通外联 JavaScript
<script src="index.js"></script>

遇到此类 <script> 标签会暂停解析 HTML 文档,先请求并执行 JS 脚本后,再继续解析 HTML 文档。

在这里插入图片描述

从这幅图可以解释上前面例子 firstEl 为什么会返回对应的 DOM 对象,而 secodeEl 会返回 null。因为执行 JS 脚本时,<div id="first"></div> 已经被解析,所以 DOM 树中存在该节点,而 <div id="secode"></div> 还未被解析,DOM 树中自然就不存在该节点,所以此时就会返回 null


2 JS 延迟加载方式

2.1 defer

『defer』

defer 这个布尔属性被设定用来通知浏览器该脚本将在 HTML 文档完成解析后,触发 DOMContentLoaded 事件前执行。— MDN


『加载过程』

外联 defer JS 脚本的加载过程:HTML 文档解析过程中遇到此类 <script> 标签不阻塞 HTML 文档的解析,而是会暂存到一个队列中,等整个 HTML 文档解析完成后再按队列的顺序请求并执行 JS 脚本。
在这里插入图片描述
『🌰 例子』

const firstEl = document.getElementById('first');
const secodeEl = document.getElementById('secode');

console.log(firstEl);  
console.log(secodeEl);  
<body>
	<script>
  		document.addEventListener("DOMContentLoaded", function () {
    		console.log("DOMContentLoaded");
  		});
	</script>
	<div id="first"></div>
	<script defer src="index.js"></script>
	<div id="secode"></div>
</body>

在这里插入图片描述
可以看到,不管是 firstEl 还是 secodeEl 都返回了对应的 DOM 对象,所以 JS 脚本是在 HTML 文档解析完成之后才执行的,另外,JS 脚本是执行在 DOMContentLoaded 事件之前的。


『注意点』

1️⃣ HTML5 规范要求脚本按照它们出现的先后顺序下载和执行。

// a.js
console.log('a');

// b.js
console.log('b');

// c.js
console.log('c');
<head>
  <script>
    document.addEventListener("DOMContentLoaded", function () {
      console.log("DOMContentLoaded");
    });
  </script>
  <script defer src="a.js"></script>
  <script defer src="b.js"></script>
  <script defer src="c.js"></script>
</head>

在这里插入图片描述

“实际中,延迟脚本并不一定会按照顺序执行。” 这句话在一些文章看到,可我运行代码发现,是按照顺序执行的。不清楚怎么回事?先记录下来吧。

2️⃣ defer 只适用于外联脚本,对内联脚本不起作用。

<head>
  <script defer>
    const firstEl = document.getElementById("first");
    const secodeEl = document.getElementById("secode");

    console.log(firstEl);
    console.log(secodeEl);
  </script>
</head>
<body>
  <div id="first"></div>
  <div id="secode"></div>
</body>

在这里插入图片描述

3️⃣ 有 defer 属性的脚本会阻止 DOMContentLoaded 事件,直到脚本被加载并且执行完成后才会触发 DOMContentLoaded 事件。

从注意点 1️⃣ 的例子中可以看到带有 defer 的 JS 脚本全部执行完才会触发 DOMContentLoaded 事件。


2.2 async

『async』

对于脚本来说,如果存在 async 属性,那么普通脚本会被并行请求,并尽快解析和执行。


『过程』

外联 async JS 脚本的加载过程:遇到此类 <script> 标签会并行下载脚本,不会阻塞 HTML 的解析。如果下载完成后 HTML 还未解析完成,则会暂停解析 HTML ,先执行 JS 脚本后,再解析 HTML。
在这里插入图片描述
但是如果 HTML 已经解析完毕,外联 async JS 脚本还未下载完成,则过程如下:

在这里插入图片描述
『🌰 例子』

// a.js
const aEl = document.getElementById('a')
console.log(aEl);

// b.js
const bEl = document.getElementById('b')
console.log(bEl);

// c.js
const cEl = document.getElementById('c')
console.log(cEl);
<head>
  <script>
    document.addEventListener('DOMContentLoaded', function () {
      console.log('DOMContentLoaded');
    })

	window.addEventListener('load', function () {
      console.log('load');
    })
  </script>
  <script src="a.js" async></script>
  <script src="b.js" async></script>
  <script src="c.js" async></script>
</head>
<body>
  <div id="a"></div>
  <div id="b"></div>
  <div id="c"></div>
</body>

在这里插入图片描述

可以看到,三个 JS 脚本会按顺序执行(如果 a.js 下载的时间久一点,就不一定会执行在 b.js 前面),但实际上,有 async 属性的 JS 脚本的执行顺序是无法预测的。并且也无法确保与DOMContentLoaded 的执行先后顺序。


『注意点』

1️⃣ 只适用于外联脚本,这一点和 defer 一致。

2️⃣ 如果有多个声明了 async 的脚本,其下载和执行也是异步的,不能确保彼此的先后顺序。

3️⃣ async 会在 load 事件之前执行,但并不能确保与 DOMContentLoaded 的执行先后顺序。


2.3 defer 和 async 异同点

相同点:

  1. 两者都只使用与外联脚本,对内联脚本不起作用。

不同点:

  1. 多个 defer JS 脚本会按顺序下载和执行
  2. 多个 async JS 脚本不会按顺序下载和执行。
  3. defer JS 脚本执行在 DOMContentLoaded 事件之前。
  4. async JS 脚本不能确保与 DOMContentLoaded 事件的执行先后顺序。

2.4 setTimeout

延迟执行 JS 代码,给网页加载留出更多时间。

『过程』
在这里插入图片描述
『🌰 例子』

<script>
  function getElement() {
    const firstEl = document.getElementById("first");
    const secodeEl = document.getElementById("secode");

    console.log(firstEl);  // <div id="first"></div>
    console.log(secodeEl); // <div id="secode"></div>
  }

 // 延迟 2s 后再执行
  setTimeout(getElement, 2000);
</script>

2.5 动态创建 DOM 方式

『过程』
在这里插入图片描述

『🌰 例子』

<body>
  <div id="first"></div>
  <div id="secode"></div>

  <script>
    function downloadJSAtOnload() {
      let el = document.createElement("script");
      el.src = "index.js";
      document.body.appendChild(el);
    }

    if (window.addEventListener)
      window.addEventListener("load", downloadJSAtOnload, false);
    else if (window.attachEvent)
      window.attachEvent("onload", downloadJSAtOnload);
    else window.onload = downloadJSAtOnload;
  </script>
</body>

2.6 将脚本元素放在文档的底部

『加载过程』
在这里插入图片描述

『🌰 例子』

<body>
  <div id="first"></div>
  <div id="secode"></div>

  <script src="index.js"></script>
</body>

3 DOMContentLoaded

当 HTML 文档被加载和解析完成后之后,DOMContentLoaded 事件就会被触发,无需等待样式表、图像和子框架的完全加载。


4 load

当整个页面及所有依赖资源如样式表和图片都已完成加载时,将触发 load 事件。

它与 DOMContentLoaded 不同,后者只要页面 HTML 解析完成之后就触发,无需等待依赖资源的加载。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值