同步加载的问题
默认的js是同步加载的,这里的“加载”可以理解成是解析、执行,而不是“下载”,在最新版本的浏览器中,浏览器对于代码请求的资源都是瀑布式的加载,而不是阻塞式的,但是js的执行总是阻塞的。这会引起什么问题呢?如果我的index页面要加载一些js,但是其中的某个请求迟迟得不到响应,于是阻塞了后面的js代码的执行(同步加载),同时页面渲染也不能继续(如果js引入是在head标签后)。
<script type="text/Javascript" src='https://china-addthis.googlecode.com/svn/trunk/addthis.js'></script> <script type="text/javascript" src='https://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script> this is a test
比如上面的这段代码,保存为index.html文件,页面的主体是一个简单的字符串,但是代码执行后页面迟迟都是空白,为何?因为请求的js迟迟无法加载(可能由于谷歌被墙等原因),于是阻塞了后面的代码的执行,页面得不到渲染。可能你会提议,把js代码放到</body>前不就能先渲染页面了!好方法,我们尝试着将js放后面:
this is a test <script type="text/javascript" src='https://china-addthis.googlecode.com/svn/trunk/addthis.js'></script> <script type="text/javascript" src='https://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>
页面瞬间被渲染,“this is a test"也很快出现在前台,世界似乎平静了,可是:
this is a test <script type="text/javascript" src='https://china-addthis.googlecode.com/svn/trunk/addthis.js'></script> <script type="text/javascript" src='https://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script> <script type="text/javascript"> console.log('hello world'); </script>
在前面代码的基础上简单加了一段代码,但是"hello world"迟迟无法在控制台输出,显然前面的js请求阻塞了后面代码的加载,我们恍然大悟,改变js的加载位置只能改变页面的渲染,然而对于js的加载并没有什么卵用,js还是会阻塞。
实现js异步加载我们的要求似乎很简单,能在页面加载的同时,在控制台输出字符串即可,再讲的通俗一点,就是在请求第一段谷歌提供的js的同时,继续执行下面的js,也就是实现js的异步加载。
最常见的做法是动态生成script标签:
<body> this is a test <script type="text/javascript"> ~function() { var s = document.createElement('script'); s.src = 'https://china-addthis.googlecode.com/svn/trunk/addthis.js'; document.body.appendChild(s); }(); </script> <script type="text/javascript" src='https://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script> <script type="text/javascript"> console.log('hello world'); </script> </body>
但是还是有点问题,这种加载方式在加载执行完之前会阻止 onload 事件的触发,而现在很多页面的代码都在 onload 时还要执行额外的渲染工作等,所以还是会阻塞部分页面的初始化处理:
<body> this is a test <script type="text/javascript"> ~function() { // function async_load() { var s = document.createElement('script'); s.src = 'https://china-addthis.googlecode.com/svn/trunk/addthis.js'; document.body.appendChild(s); // } // window.addEventListener('load', async_load, false); }(); window.onload = function() { var txt = document.createTextNode(' hello world'); document.body.appendChild(txt); }; </script> <script type="text/javascript" src='https://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script> </body>
比如上面的代码不能很好地渲染”hello world”,我们只需将注释去掉就可以了,让谷歌提供的js在onload 时才开始异步加载。这样就解决了阻塞 onload 事件触发的问题。
补充DOMContentLoaded 与 OnLoad 事件 DOMContentLoaded : 页面(document)已经解析完成,页面中的dom元素已经可用。但是页面中引用的图片、subframe可能还没有加载完。 OnLoad:页面的所有资源都加载完毕(包括图片)。浏览器的载入进度在这时才停止。这两个时间点将页面加载的timeline分成了三个阶段。
以上似乎能较好解决这个问题,但是html5提供了更简便的方法,async属性!