JavaScript异步加载浅析(2)

让脚本的加载及执行再快一步,我不想等到defer脚本一个接着一个运行(defer让我们想到一种静静等待文档加载的有序排队场景),更不想等到文档就绪之后才运行这些脚本,我想要尽快加载并且尽快运行这些脚本。这里也就想到了HTML5的async属性,但是要注意,它是一种混乱的无政府状态。

例如,我们加载两个完全不相干的第三方脚本,页面没有它们也运行得很好,而且也不在乎它们谁先运行谁后运行。因此,对这些第三方脚本使用async属性,相当于一分钱没花就提升了它们的运行速度。

async属性是HTML5新增的。作用和defer类似,即允许在下载脚本的同时进行DOM的渲染。但是它将在下载后尽快执行(即JS引擎空闲了立马执行),不能保证脚本会按顺序执行。它们将在onload 事件之前完成。

Firefox 3.6、Opera 10.5、IE 9 和 最新的Chrome 和 Safari 都支持 async 属性。可以同时使用 async 和 defer,这样IE 4之后的所有 IE 都支持异步加载,但是要注意,async会覆盖掉defer。

那么此时的页面模型如下——

复制代码 代码如下:


<!DOCTYPE html>
<html>
<head lang="en">
    <!--metadata and scriptsheets go here-->
    <script src="https://www.jb51.net/headScript.js"></script>
    <script src="https://www.jb51.net/deferredScript.js" defer></script>
</head>
<body>
    <!--content goes here-->
    <script src="https://www.jb51.net/asyncScript1.js" async defer></script>
    <script src="https://www.jb51.net/asyncScript2.js" async defer></script>
</body>
</html>

要注意这里的执行顺序——各个脚本文件加载,接着执行headScript.js,紧接着在DOM渲染的同时会在后台加载defferedScript.js。接着在DOM渲染结束时将运行defferedScript.js和那两个异步脚本,要注意对于支持async属性的浏览器而言,这两个脚本将做无序运行。

三、可编程的脚本加载

尽管上面两个脚本属性的功能非常吸引人,但是由于兼容性的问题,应用并不是很广泛。故此,我们更多使用脚本加载其他脚本。例如,我们只想给那些满足一定条件的用户加载某个脚本,也就是经常提到的“懒加载”。

在浏览器API层面,有两种合理的方法来抓取并运行服务器脚本——

1> 生成ajax请求并用eval函数处理响应

2> 向DOM插入<script>标签

后一种方式更好,因为浏览器会替我们操心生成HTTP请求这样的事。再者,eval也有一些实际问题:泄露作用域,调试搞得一团糟,而且还可能降低性能。因此,想要加载名为feture.js的脚本,我们应该使用类似下面的代码:


复制代码 代码如下:


var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.src = 'feature.js';
head.appendChild(script);

当然,我们要处理回调监听,HTML5规范定义了一个可以绑定回调的onload属性。


复制代码 代码如下:


script.onload = function() {
    console.log('script loaded ...');
}


不过,IE8及更老的版本并不支持onload,它们支持的是onreadystatechange。而且,对于错误处理仍然千奇百怪。在这里,可以多参考一些流行的校本加载库,如labjs、yepnope、requirejs等。

如下,自己封装了一个简易loadjs文件——


复制代码 代码如下:


var loadJS = function(url,callback){
    var head = document.getElementsByTagName('head')[0];
    var script = document.createElement('script');
    script.src = url;
    script.type = "text/javascript";
    head.appendChild( script);

// script 标签,IE下有onreadystatechange事件, w3c标准有onload事件
    // IE9+也支持 W3C标准的onload
    var ua = navigator.userAgent,
        ua_version;
    // IE6/7/8
    if (/MSIE ([^;]+)/.test(ua)) {
        ua_version = parseFloat(RegExp["$1"], 10);
        if (ua_version <= 8) {
            script.onreadystatechange = function(){
                if (this.readyState == "loaded" ){
                    callback();
                }
            }
        } else {
            script.onload = function(){
                callback();
            };
        }
    } else {
        script.onload = function(){
            callback();
        };
    }
};

对于document.write的方式异步加载脚本,在这里就不说了,现在很少有人这么干了,因为浏览器差异性实在是搞得头大。

要注意,使用 Image 对象异步预加载 js 文件,里面的js代码将不会被执行。

最后,谈一下requirejs中的异步加载脚本。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wgxjdw.html