昨晚‘今日头条’笔试题中遇到这道题,今天专门查了一下。
从 IE 8、Firefox 3.5、Safari 4 和 Chrome 2 开始都允许并行下载 JavaScript 文件。
<script>标签在下载外部资源时不会阻塞其他<script>标签。遗憾的是,JavaScript 下载过程仍然会阻塞其他资源的下载,比如样式文件和图片。尽管脚本的下载过程不会互相影响,但页面仍然必须等待所有 JavaScript 代码下载并执行完成才能继续。
实现延迟加载的方式有:
方式一:标签放在底部
将<script>
节点放置在</body>
之前,这样js脚本会在页面显示出来之后再加载。
方式二:Defer和Async属性
使用script
标签的defer
和async
属性
Defer属性
defer
是在html4.0中定义的
html5规范要求脚本按照出现的顺序执行,对应的js文件在页面解析到<script>
标签时开始下载,但不会执行,直到DOM
加载完成,即onload
事件触发前才会执行
当一个带有 defer
属性的 JavaScript
文件下载时,它不会阻塞浏览器的其他进程,因此这类文件可以与其他资源文件一起并行下载。
<script type="text/javascript" defer="defer" src="example.js">
<script type="text/javascript" defer="defer" src="example.js">
缺点:并不是所有浏览器支持该属性
async属性
async
是html5新增的属性,IE10和其他浏览器都支持该属性
同defer一样,不会阻塞其余资源的加载,也不会影响页面的加载,但js一旦加载好了就会执行,所以很有可能不是按照原本的顺序来执行
方式三:动态脚本元素
DOM动态创建<script>
var script = document.createElement('script');
script.type = "text/javascript";
script.src = "script.js";
document.getElementByTagName('head')[0].appendChild(script);
可以监听脚本是否加载成功
script.onload = function(){
alert('script loaded!');
};
/*IE中的监听方式不一样*/
script.onreadystatechange = function(){
if(script.readyState == 'load' || script.readyState == 'complete'){
script.onreadystatechange = null;
alert('script loaded');
}
};
方法四:使用XMLHttpRequest(XHR)
var xhr = new XMLHttpRequest();
xhr.open('get', 'script.js', true);
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
if(xhr.status >= 200 && xhr.status < 300 || xhr.status == 304){
var script = document.createElement('script');
script.type = "text/javascript";
script.text = xhr.responseText;
document.body.appendChild(script);
}
}
};
xhr.send(null);
优点:
可以下载不立即执行的javascript代码;
同样的代码在所有现代浏览器中都可以正常运行。
缺点:
javasript文件必须和页面放置在同一个域中,不能从CDN下载,所以大型网页通常都不采取XHR脚本注入技术。
总结
减少 JavaScript 对性能的影响有以下几种方法:
将所有的<script>标签放到页面底部,也就是</body>闭合标签之前,这能确保在脚本执行前页面已经完成了渲染。
尽可能地合并脚本。页面中的<script>标签越少,加载也就越快,响应也越迅速。无论是外链脚本还是内嵌脚本都是如此。
采用无阻塞下载 JavaScript 脚本的方法:
使用<script>标签的 defer 属性(仅适用于 IE 和 Firefox 3.5 以上版本);
使用动态创建的<script>元素来下载并执行代码;
使用 XHR 对象下载 JavaScript 代码并注入页面中。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。