网页控制script脚本异步执行方案,理解script标签defer属性
发布于 作者:苏南大叔 来源:程序如此灵动~
对于一个网页来说,css是放在顶部head区域加载的,而js是放在页脚body的最下方加载的,这个是根据它们的特性所决定的。对于苏南大叔的页面来说,一个页面里面不会有很多个js或者css文件,它们都被使用grunt合并成了一个文件。然后,css放顶部,js放底部,进行加载。所以理论上来说,并不会阻碍页面的加载,也不会影响页面加载线程。但是,普通的页面呢?

大家好,这里是苏南大叔的“程序如此灵动”博客,这里讲述苏南大叔和计算机代码的故事。本文讲述script标签的defer属性,本质上来说,defer是用来异步加载的。那么,它的使用方式是怎么样的呢?
本文测试环境:win10,chrome@100.0.4896.60。
基本说明
defer的意思是延迟(Deferred),并不是异步,虽然看起来效果确实是异步,但是其本意仅仅是延迟执行。给js脚本添加 defer属性,这个属性会让脚本的加载与文档的解析同步解析,然后在文档解析完成后再执行这个脚本文件,这样的话就能使页面的渲染不被阻塞。
在 HTML4.01 规范中规定:
设置后,这个布尔属性会向用户代理提示该脚本将不会生成任何网页内容(例如,JavaScript中不会生成 “document.write”),因此,用户代理可以继续解析和渲染。
测试代码
这里的测试代码,加上了type="module"的es6 module情况,有关说明文章见这里:
测试方式是通过浏览器访问web,并不是双击.html文件。
这里测试以下几种代码:
<script type="module" src="./1.js"></script>
<script type="module" defer src="./2.js"></script>
<script type="module">
import aa from './3.js'
</script>
<script type="module" defer>
import aa from './4.js'
</script>
<script defer src="./5.js"></script>
<script src="./6.js"></script>
<script defer>
console.log("7.js");
</script>
<script>
console.log("8.js");
</script>其中3.js和4.js都export了一个空函数。
console.log("3.js");
export default ()=>{};测试结果
这里先说基本原则:
defer的意思是异步执行,被设置defer的代码,在效果上会等待文档加载完毕后再执行,忽视其在页面中的物理位置。type="module"的script,直接默认就是defer的。加不加defer都是defer。- 内联的
script(就是把逻辑写在html页面里面的,不是保存为一个文件的),默认都不是defer的。加不加defer都不是defer。
然后苏南大叔在所有的被测试js前面,再加个内联的onload:
<script>
window.addEventListener('DOMContentLoaded', function () {
console.log('DOMContentLoaded')
})
</script>然后,这8种js,以及这个DOMContentLoaded,到底谁先谁后呢?答案见下图:

结论是:先执行不defer的,再执行defer,最后执行DOMContentLoaded。
特殊情况
在设置了defer的.js里面,使用document.write语句的话。会得到警告信息,并且不会正确输出.write信息。
Failed to execute 'write' on 'Document': It isn't possible to write into a document from an asynchronously-loaded external script unless it is explicitly opened.
参考文献
综述
本文讲述了script的defer属性,它可以把一些js移动到后面执行,以免阻碍页面渲染。更多js文章,可以点击: