每日一答:script标签放在 body 头部一定会造成阻塞吗?

203 阅读1分钟

script标签放在 body 头部一定会造成阻塞吗?

这是我参与2022首次更文挑战的第10天,活动详情查看:2022首次更文挑战」。

要了解是否会造成阻塞首先要了解HTML在浏览器中被打开时的解析和执行顺序

html解析执行顺序

创建一个 HTML

<!DOCTYPE html>
<html>
 
  <body style="padding: 10px">
    <div>
      <span>name</span>
      <span id="my-name">ouda</span>
    </div>
    
    <script>
      document.getElementById('my-name').innerHTML = 'changed'
   </script>
  </body>
  
</html>


上述代码中的意义是将 html 中的文本 ouda 替换成 changed

当我们将 <script> 标签写在 body的结尾,打开页面时发现确实可以实现。

image.png

然后我们修改一下代码:

<!DOCTYPE html>
<html>
 
  <body style="padding: 10px">
    <script>
      document.getElementById('my-name').innerHTML = 'changed'
   </script>
    <div>
      <span>name</span>
      <span id="my-name">ouda</span>
    </div>
    
   
  </body>
  
</html>

此时的 <script>标签被放到了 body的最前面,会得到下面的结果:

image.png

并且在控制台中会跑出错误:

Uncaught TypeError: Cannot set properties of null (setting 'innerHTML')

由此可以说明一个事实: HTML文件在浏览器中的解析是从上至下的顺序解析和执行

script 写在前面会带来的问题

了解了执行顺序,那么这么写会带来的问题就非常明显了:

  1. 页面dom没有渲染完成,可能会导致 JS 获取 dom 失败,抛出错误,造成页面阻塞。

  2. js如果承担很多同步操作耗时的操作或处理非常复杂的逻辑,会造成页面暂时卡顿,页面渲染延迟,对用户来说体验非常差

解决方案:defer 和 async

虽然不推荐这么写,但我偏要写在前面,该怎么办?这里可以使用标签的两个属性: asyncdefer

async将脚本异步执行,不会对页面的解析造成阻塞

defer规定该脚本当页面解析完成后进行执行

注意:asyncdefer属性仅仅是用于引用外部脚本,即通过src方式引入

所以我们将代码改一下:

test.html

<!DOCTYPE html>
<html>
  <script type="text/javascript" src="./test.js"  defer="defer">
 </script>
  <body style="padding: 10px">
  
    <div>
      <span>name</span>
      <span id="my-name">ouda</span>
    </div>
    
   
  </body>
  
</html>

test.js

document.getElementById('my-name').innerHTML = 'changed'

即可实现 script标签放在 body尾部的同样效果。

最后贴上一张图来说明:

1.jpg