自从开了博客,我就一下班回来匆匆吃完饭门一关等一开电脑一打开匆匆的研究东西,以至于朋友们都怀疑我是不是都得了自闭症
其实因为我有恐惧心理怕自己的技术哪天跟不上社会了,说到技术我觉得技术不求越新越好,但求越稳越好,就目前而言,前段框架五花八门,八面玲珑,一一都会
别吹牛了,在我看来找到自己的框架吃透它。不吹牛了,回归正题。
============================================================================================我们今天看下JS的脚本延迟
首先先看下浏览器是如何处理脚本的
1.浏览器一边下载HTML网页,一边开始解析
2.解析过程中,发现<script>标签
3.暂停解析,网页渲染的控制权转交给JavaScript引擎
4.如果<script>标签引用了外部脚本,就下载该脚本,否则就直接执行
5.执行完毕,控制权交还渲染引擎,恢复往下解析HTML网页
如果外部脚本加载时间很长(比如一直无法完成下载),就会造成网页长时间失去响应,浏览器就会呈现“假死”状态,这被称为“阻塞效应”。
为了避免这种情况,较好的做法是将<script>
标签都放在页面底部,而不是头部。
怎样才能阻止这种状态呢
一下属性来解决
1.anync:改属性表示立即下载脚本,但不应妨碍页面的其他操作,改脚本只对外部脚本有效,属于异步脚本处理
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <script src="a.js" async> </script> <script src="b.js" async></script> </head> <body> <div> <p> HELLO world </p> </div> </body> </html>
async属性的作用是,使用另一个进程下载脚本,下载时不会阻塞渲染。
浏览器如何渲染呢?
浏览器开始解析HTML网页 解析过程中,发现带有async属性的script标签 浏览器继续往下解析HTML网页,同时并行下载script标签中的外部脚本 脚本下载完成,浏览器暂停解析HTML网页,开始执行下载的脚本 脚本执行完毕,浏览器恢复解析HTML网页
注意:async属性可以保证脚本下载的同时,浏览器继续渲染。需要注意的是,一旦采用这个属性,就无法保证脚本的执行顺序。哪个脚本先下载结束,就先执行那个脚本。
2..defer
defer也属于脚本延迟,其区别在于浏览器遇到该属性是不立即执行脚本而是等到加载到</html>在执行脚本,这样大大减少了浏览器假死的状态
步骤
浏览器开始解析HTML网页 解析过程中,发现带有defer属性的script标签 浏览器继续往下解析HTML网页,同时并行下载script标签中的外部脚本 浏览器完成解析HTML网页,此时再执行下载的脚本
<script src="a.js" defer></script> <script src="b.js" defer></script>
相比之下async的级别 >defer
==========================================================================================================================================
defer属性知多少
目前所有浏览器都支持defer属性,但是Chrome和Firefox中只有在加载外部脚本时defer才会生效,行内脚本使用defer是没有作用的。而IE中不论什么情况,defer都有效。
defer的作用就是阻止脚本在下载完成后立刻执行,它会让脚本延迟到所有脚本加载执行完成后,在DOMContentLoaded之前执行,通俗的说就是顺序加载延迟执行。虽然都是在DOMContentLoaded之前执行,但是在不同浏览器之间,执行的各种脚本执行的顺序还是不一样的。看下面这个例子:
<html> <meta charset="utf-8"> <head> <script type="text/javascript"> var result = "" ; var head = document.getElementsByTagName("head")[0] ; //DOMContentLoaded if(window.addEventListener){ document.addEventListener("DOMContentLoaded",function(){ result += "DOMContentLoaded\n" ; }) ; }else{ document.attachEvent("onDOMContentLoaded",function(){ result += "DOMContentLoaded\n" ; }) ; } window.onload = function(){ result += "window loaded\n"; //console.log("window loaded") ; } ; </script> <!--头部行内延迟脚本--> <script type="text/javascript" defer = "defer"> result += "Head Inline Script defer\n" ; </script> <!--头部行内脚本--> <script type="text/javascript"> result += "Head Inline Script\n" ; </script> <!--头部外部延迟脚本 External Head Script defer--> <script type="text/javascript" src = "external_head_defer.js" defer="defer"></script> <!--头部行内脚本 External Head Script--> <script type="text/javascript" src = "external_head.js"></script> </head> <body> <button>SHOW</button> <!--Body行内延迟脚本--> <script type="text/javascript" defer = "defer"> result += "Body Inline Script defer\n" ; </script> <!--Body行内脚本--> <script type="text/javascript"> result += "Body Inline Script\n" ; </script> <!--Body外部延迟脚本 External Body Script defer--> <script type="text/javascript" defer = "defer" src = "external_body_defer.js"></script> <!--Body外部脚本 External Body Script--> <script type="text/javascript" src = "external_body.js"></script> <script type="text/javascript"> document.getElementsByTagName("button")[0].onclick = function(){console.log(result);} ; </script> </body> </html>
从上面可以看出几个问题:
首先,IE9以下不支持DOMContentLoaded(后面会说明这个情况)
其次,验证了上面说的Chrome和Firefox行内脚本不支持defer属性
最后,defer确实达到了延迟执行的目的,没有阻塞后面脚本的加载和执行。但是耗时的操作还是会阻塞DOMContentLoaded事件,而大多数情况下大家都会把页面初始化的脚本附加在DOMContentLoaded事件上,所以defer方法还是不能很好解决这个问题
END
虽然通过脚本后置和异步加载可以降低脚本加载对页面的影响,但是就算是实现了异步加载,但是由于浏览器的脚本解析的单线程的,所以脚本执行的时候仍然会阻塞整个页面(当然除了使用Web Worker),这时候用户是无法完成正常交互的,所以要想真正彻底的优化页面加载,还需要从代码的优化开始。