在浏览器中网页加载中 javascript 的 加载 和 执行会默认阻塞 DOM 的加载和页面的渲染。
因此,在编写代码的时候我们往往将 script 标签放到 body 的最后面。
当然,也可以通过异步创建 script 标签的方式来实现 js的异步加载。
只是,这些都是通过绕路的方式实现的。
如何让脚本本身不阻塞页面(异步)来加载,是一个常态化的需求。
为此,在 HTML4.1 规范中增加了一个 defer 属性来解决这个问题。
script 标签的 defer 属性
HTML4.1规范规定,只需要给 script 加上 defer 属性,脚本就可以延迟到文档完全被解析和显示之后执行。
1 | <html> |
继HTML4.1规范之后,HTML5 也在之前的规范基础上补充和完善了几条规则
1 | defer 属性只对外部脚本文件有效。 (在 IE7 及更早的版本中,对行内脚本也可以指定这个属性。) |
因此,正常情况下,按照规范
1 | 如上 DOM 结构中,example1.js 和 example2.js 脚本会在 DOM 渲染的时候同步下载,并不会阻塞 DOM 的加载。 |
然而,规范是规范,有了规范也得有浏览器产商遵循才行,对于 defer 属性也有部分浏览器并没有按照上述规范执行。
比如:
1 | 在多个 script 加了 defer 属性的情况下,执行顺序不一定是 script 标签出现的顺序; |
因此,稳妥起见,即便加了refer,最好还是将脚本放到 body 的最后。
HTML5 规范除了补充了 defer 的规则,本身也新增了一个新的属性 async。
script 标签的 async 属性
1 | <html> |
从改变脚本的处理来看,async 和 补充版本的 defer 类似,都是为了异步加载 javascript 而存在的。
但是也有一些区别
1 | 最明显的区别是 defer 执行按照 script 标签的出现顺序,而 async 执行顺序是不确定的。 |
总结
1.异步脚本不会阻塞DOM,而且保证会在页面的 load 事件前执行,但可能会在 DOMContentLoaded 之前或之后。
2.正因为加了 defer 或者 async 的脚本不会阻塞 DOM 的加载,所以,内部不应该有操作 DOM 的行为。
2.defer 脚本下载和执行都不会阻塞DOM。
3.多个 async 的脚本并不会保证按照它们在文档中的先后顺序执行,因此,多个 async 的脚本之间不应该有依赖关系。
4.async 的脚本下载和解析不会阻塞 DOM,解析完成之后执行的时候会阻塞 DOM
最后引用网上的一张图
附录(同步脚本插入)
1 | var script = document.createElement('script'); |
执行结果:before script、from script、after script
原因:
1 | javascript 脚本通过上面的方式插入到 DOM 的时候会立即执行 |