异步加载js

      加载 JS 最常见的方法就是在<head>中插入<script type="text/javascript" src="balabala"></script>在浏览器解析过程中,一旦发现<script>,就会阻止浏览器的后续处理。直至等待脚本下载并执行完后,才继续恢复解析 HTML 网页。这是因为 JS 中可能会修改 DOM 还有重定向行为等。所以默认同步执行才是安全的,否则会导致复杂的线程竞赛问题。所以一般建议把<script>放在<body>结尾处,这样尽可能减少页面阻塞。如果 JS 是来源于网页开发者自己的 JS 代码,内容是开发者可以控制的。那么我们可以采用以下三种方式去提高性能:

1、合并文件:当有多个script标签引用时,我们根据需求合并至单个。这样有助于建立一次 TCP 连接,就可完成下载。
2、压缩文件:利用压缩工具压缩 JS,去除多余的空格和注释,减少文件大小。
3、末尾加载:把<script>写在<body>结尾处,因为整个文档正文已经加载完毕。

如果我们使用的是第三方 JS,类似高德地图等由其他服务提供商提供的,这时 JS 代码并不受我们控制,就很有可能出现加载时间很长,甚至失败的情况。时间太长则会造成浏览器“假死”,页面一片空白。如果第三方 JS 代码越多,则这种风险就越大。所以就要考虑如果在加载第三方 JS 的情况下,保证自己网站的加载速度。如果这个时间还采用同步加载,带来的体验往往并不好。所以我们可以采用异步加载这些第三方 JS。

async 属性

1
2
<!-- 指定async属性,异步加载完马上执行。以及 onload 回调-->
<script type="text/javascript" src="balabala.js" async="async" onload="init()"></script>

      async是 HTML5 新增的属性,IE10 和大部分浏览器都是支持该属性的。该属性的作用是让脚本能异步加载,也就是会开启一个线程去加载 JS,会在下载完后立即执行。但是一些旧浏览器并不支持。从而导致 JS 文件在这些浏览器中不是异步的,并且会阻止掉页面渲染。但是如果我们做的是移动端的产品,移动端的浏览器大多支持aysnc,我们就可以大胆放心的用啦。带有async属性的脚本在 script 文件下载完成后会立即执行,并且其执行时间一定在 window 的 load 事件触发之前。所以这意味着多个带有async属性的脚本并不保证会按其在页面中的出现先后顺序执行。因此要确保带有async属性的脚本之间互不依赖非常重要。如果对执行的先后顺序有要求,那让我们来看看defer

defer 属性

1
2
<!-- 指定defer属性,异步加载完延迟执行。-->
<script type="text/javascript" src="balabala.js" defer="defer"></script>

      这个属性和async类似,都用于改变处理脚本的行为。都是只适用于外部脚本文件,都会开启一个线程去下载 JS 文件。但是与async不同的是,defer会告诉浏览器延迟执行。就是在 DOM 渲染后之后再按顺序执行 JS。也就是说 async 是乱序的,而 defer 是顺序执行。

要注意的是两者都不应该使用 document.write,这个导致整个页面被清除

defer 和 async 一起用

1
<script type="text/javascript" src="balabala.js" async="async" defer="defer"></script>

      测试后发现在谷歌浏览器上表现和只使用async一致.当浏览器同时使用了 asyncdefer 属性之后,浏览器会忽略 defer属性.对于不支持 async 的浏览器,会自动使用defer属性

动态创建 script 标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function loadScript(url, callback) {
var script = document.createElement("script");
script.type = "text/javascript";
if (typeof callback != "undefined") {
if (script.readyState) {
//IE
script.onreadystatechange = function() {
if (
this.readyState == "complete" ||script.readyState == "loaded"
) {
script.onreadystatechange = null;
callback();
}
};
} else {
//其他浏览器
script.onload = function() {
callback();
};
}
}
script.src = url;
document.body.appendChild(script);
}

如果需要失败的回调,可以通过设置定时器的方式来实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//失败成功回调
var timer;
function time(){
timer=setTimeout(function(){
if(timer){
timer="";
//定时器未在指定时间内收到请求,终止调用,执行错误回调
}else{
//调用成功
}
},20000);
loadScript("balabala.js",set);
}
var set = function(){
if(timer){
clearTimeout(timer);
timer="";
//收到请求,执行成功回调并清空定时器
}else{
//收到清空,已执行错误回调
}
}

      这种方法是在页面中<script>标签内,用 js 创建一个 script 元素并插入到 document 中。这样就做到了非阻塞的下载 js 代码。

总结

      以上列出的三种方案。动态创建 script 标签是目前兼容性最好、普适性最高的方案。如果我们的用户大多是移动端或者不需要兼容 IE10 以下的浏览器,那么async属性使最合适的。如果我们不介意 JS 的执行会被延后到文档解析完毕后再执行,而且对加载顺序有所要求,那么defer是最合适的方案。

Copyright ©2019 guowj All Rights Reserved.

访客数 : | 访问量 :