在网页开发中,HTML脚本加载阻塞渲染是个常见问题。今天就来聊聊解决这个问题的两个利器:async和defer属性,看看它们的区别和使用场景。

一、HTML脚本加载阻塞渲染问题

在网页里,HTML代码是从上到下依次解析的。当遇到<script>标签时,浏览器会暂停HTML的解析,先去下载并执行脚本,等脚本执行完了,才会接着解析剩下的HTML。这就导致页面渲染被阻塞,用户可能要等好一会儿才能看到完整的页面。

举个例子:

<!-- HTML技术栈 -->
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>脚本加载阻塞示例</title>
    <!-- 这里的脚本会阻塞渲染 -->
    <script src="blocking-script.js"></script>
</head>

<body>
    <h1>这是一个测试页面</h1>
    <p>这里是一些文本内容。</p>
</body>

</html>

在这个例子中,浏览器会先下载并执行blocking-script.js,在这期间,<h1><p>标签的内容不会显示出来,直到脚本执行完毕。

二、async属性

1. 基本概念

async属性就像是一个急性子,它让脚本以异步的方式加载。也就是说,浏览器在遇到带有async属性的<script>标签时,会继续解析HTML,同时去下载脚本。等脚本下载完成后,会立即停止HTML解析,执行脚本,执行完再接着解析HTML。

2. 示例

<!-- HTML技术栈 -->
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>async属性示例</title>
    <!-- 带有async属性的脚本 -->
    <script async src="async-script.js"></script>
</head>

<body>
    <h1>这是一个测试页面</h1>
    <p>这里是一些文本内容。</p>
</body>

</html>

在这个例子中,浏览器会一边解析HTML,一边下载async-script.js。如果脚本下载快,可能在HTML解析到一半时就下载完了,这时会暂停HTML解析,执行脚本,执行完再接着解析。

3. 应用场景

适合那些和页面渲染关系不大的脚本,比如统计代码、广告脚本等。这些脚本不影响页面的正常显示,使用async属性可以让它们在后台默默下载,不影响用户看到页面内容。

4. 技术优缺点

  • 优点:不会阻塞HTML解析,能让页面更快地显示出来,提高用户体验。
  • 缺点:脚本执行顺序无法保证。因为是异步加载,哪个脚本先下载完就先执行哪个,可能会导致依赖关系混乱。

5. 注意事项

如果脚本之间有依赖关系,就不能用async属性,不然可能会出错。

三、defer属性

1. 基本概念

defer属性就像是一个有耐心的人,它也让脚本以异步的方式加载,但会等HTML解析完后再执行脚本。也就是说,浏览器在遇到带有defer属性的<script>标签时,会继续解析HTML,同时去下载脚本。等HTML解析完了,再按照脚本在HTML中出现的顺序依次执行。

2. 示例

<!-- HTML技术栈 -->
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>defer属性示例</title>
    <!-- 带有defer属性的脚本 -->
    <script defer src="defer-script.js"></script>
</head>

<body>
    <h1>这是一个测试页面</h1>
    <p>这里是一些文本内容。</p>
</body>

</html>

在这个例子中,浏览器会一边解析HTML,一边下载defer-script.js。等HTML解析完了,才会执行这个脚本。

3. 应用场景

适合那些需要在HTML解析完后执行的脚本,比如操作DOM元素的脚本。因为在HTML解析完后,DOM树已经构建好,这时执行脚本可以准确地操作DOM元素。

4. 技术优缺点

  • 优点:不会阻塞HTML解析,并且能保证脚本执行顺序和它们在HTML中出现的顺序一致。
  • 缺点:如果脚本下载时间过长,会延迟脚本的执行。

5. 注意事项

defer属性只对外部脚本有效,对内联脚本无效。

四、async与defer属性的区别

1. 执行顺序

  • async属性的脚本执行顺序不确定,哪个先下载完就先执行哪个。
  • defer属性的脚本会按照它们在HTML中出现的顺序依次执行。

2. 对HTML解析的影响

  • async属性的脚本在下载完成后会立即停止HTML解析,执行脚本。
  • defer属性的脚本会等HTML解析完后再执行,不会打断HTML解析过程。

3. 示例对比

<!-- HTML技术栈 -->
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>async与defer对比示例</title>
    <!-- async脚本 -->
    <script async src="async-script1.js"></script>
    <script async src="async-script2.js"></script>
    <!-- defer脚本 -->
    <script defer src="defer-script1.js"></script>
    <script defer src="defer-script2.js"></script>
</head>

<body>
    <h1>这是一个测试页面</h1>
    <p>这里是一些文本内容。</p>
</body>

</html>

在这个例子中,async-script1.jsasync-script2.js的执行顺序不确定,而defer-script1.js会先于defer-script2.js执行。

五、使用场景总结

1. 当脚本和页面渲染无关时

可以使用async属性,比如统计代码、广告脚本等。这样可以让页面更快地显示出来,不影响用户体验。

2. 当脚本需要操作DOM元素时

使用defer属性。因为在HTML解析完后,DOM树已经构建好,这时执行脚本可以准确地操作DOM元素。

3. 当脚本之间有依赖关系时

使用defer属性,因为它能保证脚本按照顺序执行,避免依赖关系混乱。

六、总结

在解决HTML脚本加载阻塞渲染问题时,asyncdefer属性是两个非常有用的工具。async属性适合那些和页面渲染无关的脚本,能让页面更快地显示出来,但执行顺序不确定;defer属性适合需要在HTML解析完后执行的脚本,能保证脚本执行顺序,避免依赖关系混乱。在实际开发中,要根据具体需求选择合适的属性,以提高页面的性能和用户体验。