高性能JavaScript异步加载

脚本的阻塞性

在web开发中,针对JavaScript的性能的讨论,离不开它的阻塞性,一般情况下,当前JavaScript 在下载和运行时,后续的JavaScript引用、文档节点和样式都不能被浏览器处理,我们说JavaScript具有串行性或者阻塞性。这将导致用户只能等待才能看到最终页面,如果脚本过大,还可能影响用户体验。
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>编程之美</title>
    <script type="text/javascript" src="script1.js"></script>
    <script type="text/javascript" src="script2.js"></script>
</head>
<body>
    这里是文档正文部分。
</body>
</html>

正如上面的文档结构,浏览器会首先处理head部分,下载script1.js->执行script1.js->下载script2.js->执行script2.js->后续文档处理->完毕,最后处理head下面的节点,在脚本未执行完毕时,后续工作都需等待,虽然在最新的浏览器中都支持并行下载js文件,但浏览器仍然无法并行执行,因为浏览器UI是单线程的,即是script1和script2能够同时下载,但他们也必须保持script1执行完毕后才能执行script2,最后才能渲染界面。

不阻塞的脚本

为了提高性能,我们通常会采用如下的做法:

1、合并文件:如果有多个script引用,我们将文件合并至单个,这样有助于建立一次TCP连接,即可下载。

2、压缩文件:将格式化的JavaScript代码压缩,这样可以删除js文件中的空白字符,从而减少文件大小。

3、文档末尾加载:将JavaScript引用写在</body>结束标签之前,因为整个文档正文已经加载完毕。

延期执行脚本

如果您一定要将script标签放置在head中,那么建议给script标签添加一个无值的defer属性,如下所示:

<script type="text/javascript" src="script1.js" defer></script>
<script type="text/javascript" src="script2.js" defer></script>

这样的代码将使得浏览器按照:下载script1.js->下载script2.js->文档加载呈现->执行script1.js->执行script2.js->完毕,当然下载仍然是阻塞性的,只是执行过程被放到了文档加载完毕之后,但defer属性并非所有浏览器都能够支持。

动态加载脚本

JavaScript的强大在于它能够动态操纵DOM元素,利用这点特点,可以在文档加载完毕后,动态创建script标签,方法封装如下。

<script>
    function loadScript(url, callback) {
        var script = document.createElement("script")
        script.type = "text/javascript";
        if (script.readyState) { //IE浏览器状态判断
            script.onreadystatechange = function () {
                if (script.readyState == "loaded" || script.readyState == "complete") {
                    script.onreadystatechange = null;
                    callback();
                }
            };
        }
        else { //其它浏览器状态判断
            script.onload = function () {
                callback();
            };
        }
        script.src = url;
        document.getElementsByTagName("head")[0].appendChild(script);
    }
</script>


上面的代码是零度封装的函数,您可以在文档末尾调用loadScript函数动态加载脚本,该函数第一个参数url表示js文件地址,第二个参数callback表示脚本加载完毕后需要回调的自定义函数,该函数兼容所有浏览器,虽然可以动态加载脚本,如果需要加载多个js文件,浏览器可能无法保证加载的次序,如果您的脚本引用具有顺序性,可以使用如下的方式调用。
<script>
    loadScript("script1.js", function () {
        loadScript("script2.js", function () {
          //script1.js和script2.js都已下载完毕
        });
    });
</script>

上面的代码首先会下载script1.js,在script1.js加载完毕的后回函数中下载script2.js,然后执行回调函数。

脚本动态注入

另外一种异步加载script的方式是使用AJAX,通过异步方式下载js文件,然后将js内容注入到script标签内,然后执行。

<script>
    var xhr = new XMLHttpRequest();
    xhr.open("get", "script1.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);
</script>
上面的代码通过AJAX请求get方式下载script1.js文件,当HTTP状态返回正常时,将脚本内容追加到script标签内,注入脚本会立即执行。如果您使用jQuery编程,那么异步加载一个脚本将非常简单:

<script>
    $.getScript("script1.js", function (data, status, jqxhr) {
        //脚本加载完毕,执行其它操作
    });
</script>


上面两种AJAX异步加载脚本的示例看上去很完美,但它有一个缺陷就是AJAX不能跨域,如果您所处的域名和脚本域名不在同一个域名之下,那么这种方案将不能够被采纳。针对AJAX跨域有一些解决方案,但不是本文讨论的范围,如果您想通过AJAX跨域访问,可学习关于JSONP的相关知识,它会告诉您如何进行AJAX跨域编程。

当然如果异步加载的脚本文件过多,脚本依赖性的管理将变得非常困难,模块化的JavaScript编程方式将越来越被开发者重视,类似于CommonJS、AMD、NodeJS、RequireJS和SeaJS的框架将被广大网站所使用,建议大家学习,异步编程,按需加载是当今编程的主题词。感谢阅读本文,希望对您有所帮助。


转载:http://www.xcode.me/more/java-script-async-load



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值