页面渲染时js阻塞的解决方法
一般地,一个包含外部样式表文件和外部脚本文件的HTML载入和渲染过程是这样的:
- 浏览器下载HTML文件并开始解析DOM。
- 遇到样式表文件
link[rel=stylesheet]
时,将其加入资源文件下载队列,继续解析DOM。 - 遇到脚本文件时,暂停DOM解析并立即下载脚本文件。
- 下载结束后立即执行脚本,在脚本中可访问当前
<script>
以上的DOM。 - 脚本执行结束,继续解析DOM。
- 整个DOM解析完成,触发
DOMContentLoaded
事件。
什么是阻塞?
在页面中我们通常会引用外部文件,而浏览器在解析HTML页面是从上到下依次解析、渲染,如果<head>中引用了一个a.js文件,而这个文件很大或者有问题,需要2秒加载,那么浏览器会停止渲染页面(此时是白屏显示,就是页面啥都没有),2秒后加载完成才会继续渲染,这个就是阻塞。
为什么会阻塞?
因为浏览器不知道a.js中执行了哪些脚本,会对页面造成什么影响,所以浏览器会等js文件下载并执行完成后才继续渲染,如果这个时间过长,会白屏。
解决方法
1、推迟加载(延迟加载)
如果页面初始的渲染并不依赖于js或者CSS可以用推迟加载,就是最后在加载js和css,把引用外部文件的代码写在最后。比如一些按钮的点击事件,比如轮播图动画的脚本也可以放在最后。
2、defer延迟加载
<script src="" defer></script>
在文档解析完成开始执行,并且在DOMContentLoaded事件之前执行完成,会按照他们在文档出现的顺序去下载解析。效果和把script放在文档最后</body>之前是一样的。
注:defer最好用在引用外部文件中使用,用了defer不要使用document.write()方法;使用defer时最好不要请求样式信息,因为样式表可能尚未加载,浏览器会禁止该脚本等待样式表加载完成,相当于样式表阻塞脚本执行。
3、异步加载
- async异步加载:就是告诉浏览器不必等到加载完外部文件,可以边渲染边下载,什么时候下载完成什么时候执行。
<script type="text/javascript" src="a.js" async></script>
注:用了async不要使用document.write()方法;使用async时最好不要请求样式信息,原因和defer一样。
- script dom element法:这个方法是用js动态创建一个script元素添加在document中。
<script type="text/javascript"> (function() { var s = document.createElement('script'); s.type = 'text/javascript'; s.async = true; //这句可以删除,但是效果不变。 s.src = 'js/a.js'; var x = document.getElementsByTagName('script')[0]; x.parentNode.insertBefore(s, x); })(); </script>
注意:这种方法会阻止onload事件
- onload时异步加载:这个和script dom element法差不多但是他不是同时执行js和html,他是等html的文件,图片之类的、页面所有的资源全部加载完成后再下载执行js,这样的方法可以避免阻塞onload事件的触发。
(function() { function async_load(){ var s = document.createElement('script'); s.type = 'text/javascript'; s.async = true; s.src = 'js/yibujiaz.js'; var x = document.getElementsByTagName('script')[0]; x.parentNode.insertBefore(s, x); } if (window.attachEvent) window.attachEvent('onload', async_load); else window.addEventListener('load', async_load, false); })();