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
的结尾,打开页面时发现确实可以实现。
然后我们修改一下代码:
<!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
的最前面,会得到下面的结果:
并且在控制台中会跑出错误:
Uncaught TypeError: Cannot set properties of null (setting 'innerHTML')
由此可以说明一个事实: HTML文件在浏览器中的解析是从上至下的顺序解析和执行
script 写在前面会带来的问题
了解了执行顺序,那么这么写会带来的问题就非常明显了:
-
页面dom没有渲染完成,可能会导致 JS 获取 dom 失败,抛出错误,造成页面阻塞。
-
js如果承担很多同步操作耗时的操作或处理非常复杂的逻辑,会造成页面暂时卡顿,页面渲染延迟,对用户来说体验非常差
解决方案:defer 和 async
虽然不推荐这么写,但我偏要写在前面,该怎么办?这里可以使用标签的两个属性: async
和 defer
async
将脚本异步执行,不会对页面的解析造成阻塞
defer
规定该脚本当页面解析完成后进行执行
注意:async
和 defer
属性仅仅是用于引用外部脚本,即通过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
尾部的同样效果。
最后贴上一张图来说明: