Script放在body中间会阻塞吗

默认script标签是同步执行,因此会发生阻塞

浏览器解析html文件时,从上向下解析,解析到DOM中的script时会暂停DOM构建,在脚本加载并执行完毕后才会继续向下解析

因此可以看到,JS脚本存在会阻塞DOM解析的问题进而影响页面渲染速度


实验

<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport"
        content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>js单线程阻塞</title>
</head>
<body>
<h1>111</h1>
<script src="index.js"></script>
<h1>222</h1>
</body>
</html>

index.js

const startTime = new Date().getTime()
let endTime = ''

do {
    
    
  endTime = new Date().getTime()
} while (endTime - startTime <= 2000)

在浏览器中打开可以看到,script 之上的dom渲染完成,在加载和运行 script 时耗费了 2s后,script 后面的的 dom 才加载

在这里插入图片描述


解决方案

1. script 放在 body 最下方

<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport"
        content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>js单线程阻塞</title>
</head>
<body>
<h1>111</h1>
<h1>222</h1>
<!-- script 放在 body 最下方 -->
<script src="index.js"></script>
</body>
</html>

script 放在 body 最下方,所有的dom加载完成后才加载、执行script,因此不会阻塞页面的渲染,如下图
在这里插入图片描述


2. 使用 async 异步加载 script

dom解析时,遇到设置了async的脚本,就会在后台进行下载,但是并不会阻止dom的渲染。

当页面解析并且渲染完毕后,在Load 事件触发前执行,async脚本的加载不计入DOMContentLoaded事件统计

<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport"
        content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>js单线程阻塞</title>
</head>
<body>
<h1>111</h1>
<!-- 使用 async 异步加载 script -->
<script async src="index.js"></script>
<h1>222</h1>
</body>
</html>

index.js

const startTime = new Date().getTime()
let endTime = ''

do {
    
    
  endTime = new Date().getTime()
} while (endTime - startTime <= 2000)

debugger

下图可以看到,在执行断点前,dom已经渲染完毕,不会阻塞页面

在这里插入图片描述


3. 使用 defer 异步加载 script

dom解析时,遇到设置了defer的脚本,就会在后台进行下载,但是并不会阻止dom的渲染,当页面解析并且渲染完毕后。

会等到所有的defer脚本加载完毕并按照顺序执行,执行完毕后会触发DOMContentLoaded事件。

<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport"
        content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>js单线程阻塞</title>
</head>
<body>
<h1>111</h1>
<!-- 使用 defer 异步加载 script -->
<script defer src="index.js"></script>
<h1>222</h1>
</body>
</html>

下图可以看到,在执行断点前,dom已经渲染完毕,也不会阻塞页面
在这里插入图片描述


4. WebWorker

<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport"
        content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>js单线程阻塞</title>
</head>
<body>
<h1>111</h1>
<!-- WebWorker -->
<script>
  const worker  = new Worker('./index.js')
  worker.postMessage("兄弟,帮我运行下这个脚本")
  worker.onmessage = (e) => {
      
      
    console.log(e);
  }
</script>
<h1>222</h1>
</body>
</html>

index.js

const startTime = new Date().getTime()
let endTime = ''

do {
    
    
  endTime = new Date().getTime()
} while (endTime - startTime <= 2000)

onmessage = (e) => {
    
    
  console.log(e);

  postMessage("好的大兄弟")
}

下图可以看到,在执行断点前,dom已经渲染完毕,不会阻塞页面
在这里插入图片描述

在这里插入图片描述

总结

问题

浏览器解析html文件时,从上向下解析,解析到DOM中的script时会暂停DOM渲染,在脚本加载并执行完毕后才会继续向下解析。因此JS脚本存在会阻塞DOM解析的问题进而影响页面渲染速度

解决方案

  1. 将script 放在 body 最下方
  2. 使用 async 异步加载 script
  3. 使用 defer 异步加载 script
  4. WebWorker

async、defer 推荐应用场景

  • 如果你的JS代码依赖于页面中的DOM元素,或者被其他脚本文件依赖,应当使用 defer

  • 如果你的脚本并不关心页面中的DOM元素,并且也不会产生其他脚本需要的数据,可以使用 async

  • 如果不太确定的话,选择defer 会比 async 更靠谱

猜你喜欢

转载自blog.csdn.net/qq_41887214/article/details/124909219
今日推荐