在 js 引擎部分,我们可以了解到,当渲染引擎解析到 script 标签时,会将控制权给 JS 引擎,如果 script 加载的是外部资源,则需要等待下载完后才能执行。 所以,在这里,我们可以对其进行很多优化工作。
为了让渲染引擎能够及早的将 DOM 树给渲染出来,我们需要将 script 放在 body 的底部,让页面尽早脱离白屏的现象,即会提早触发 DOMContentLoaded 事件. 但是由于在 IOS Safari, Android browser 以及 IOS webview 里面即使你把 js 脚本放到 body 尾部,结果还是一样。 所以这里需要另外的操作来对 js 文件加载进行优化.
这是 html4 中定义的一个 script 属性,它用来表示的是,当渲染引擎遇到 script 的时候,如果 script 引用的是外部资源,则会暂时挂起,并进行加载。 渲染引擎继续解析下面的 HTML 文档,解析完时,则会执行 script 里面的脚本。
- <script src="outside.js" defer></script>
他的支持度是 <=IE9 的.
并且,他的执行顺序,是严格依赖的,即:
- <script src="outside1.js" defer></script>
- <script src="outside2.js" defer></script>
当页面解析完后,他便会开始按照顺序执行 outside1 和 outside2 文件。
如果你在 IE9 以下使用 defer 的话,可能会遇到 它们两个不是顺序执行的,这里需要一个 hack 进行处理,即在两个中间加上一个空的 script 标签
- <script src="outside1.js" defer></script>
- <script></script> //hack
- <script src="outside2.js" defer></script>
async 是 H5 新定义的一个 script 属性。 他是另外一种 js 的加载模式。
可以看出 async 也可以解决 阻塞加载 这个问题。不过,async 执行的时候是异步执行,造成的是,执行文件的顺序不一致。即:
- <script src="outside1.js" async></script>
- <script src="outside2.js" async></script>
- 这时,谁先加载完,就先执行谁。所以,一般依赖文件就不应该使用async而应该使用defer.
defer 的兼容性比较差,为 IE9+ , 不过一般是在移动端使用,也就不存在这个 problem 了。
脚本异步是一些异步加载库 (比如 require) 使用的基本加载原理. 直接上代码:
- function asyncAdd(src) {
- var script = document.createElement('script');
- script.src = src;
- document.head.appendChild(script);
- }
- //加载js文件
- asyncAdd("test.js");
这时候,可以异步加载文件,不会造成阻塞的效果.
但是,这样加载的 js 文件是无序的,无法正常加载依赖文件。
这时候,我们需要对上述函数进行优化.
- var asyncAdd = (function(){
- var head = document.head,
- script;
- return function(src){
- script = document.createElement('script');
- script.src= src;
- script.async=false;
- document.head.appendChild(script);
- }
- })();
- //加载文件
- asyncAdd("first.js");
- asyncAdd("second.js");
- //或者简便一点
- ["first.js","second.js"].forEach((src)=>{async(src);});
但是,使用脚本一步加载的话,需要等待 CSS 文件加载完后,才开始进行加载,不能充分利用浏览器的并发加载优势。而使用静态文本加载 async 或者 defer 则不会出现这个问题。
使用脚本异步加载时,只能等待 css 加载完后才会加载
使用静态的 async 加载时,css 和 js 会并发一起加载
关于这三种如何取舍,那就主要看 leader 给我们目标是什么,是兼容 IE8,9 还是手机端,还是桌面浏览器,或者两两组合。
但是对于单独使用某一个技能的场景,使用时需要注意一些 tips。
js 文件放置位置应该放置到 body 末尾
如果使用 async 的话,最后加上 defer 以求向下兼容
- <script src="test.js" async defer></script> //如果两者都支持,async会默认覆盖掉defer
- //如果只支持一个,则执行对应的即可
通常,我们使用的加载都是 defer 加载 (因为很强的依赖关系).
来源: https://www.cnblogs.com/csd97/p/8178339.html