第1章:加载和执行

脚本位置

组织脚本

无阻塞的脚本

延迟的脚本

任何带有defer属性的<script>元素在DOM完成加载之前都不会被执行。

动态脚本元素

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

浏览器那去在<script>元素接收完成时触发一个load事件,可以通过侦听此事件来获得脚本加载完成时的状态。

var script = document.createElement('script');
script.onload = function () {
    console.log("Script.loaded!");
};
script.src = "file.js";
document.getElementsByTagName("head")[0].appendChild(script);
//hello
//Script loaded!

IE支持另一种实现方式,它会触发一个readystatechange事件。

<script>元素提供一个readyState属性,它的值在外链文件的下载过程的不同阶段会发生变化,该属性有5中取值:

  1. uninitialized 初始状态
  2. loading 开始下载
  3. loaded 下载完成
  4. interactive 数据完成下载但尚不可用
  5. complete 所有数据已准备就绪

使用readystatechange事件最靠谱的方式是同时检查这两种状态,只要其中任何一个触发,就删除事件处理器(确保事件不会处理两次)。

var script = document.createElement('script');
script.onreadystatechange = function () {
    if (script.readyState === "loaded" || script.readyState === "complete") {
        script.onreadystatechange = null;
        console.log("script loaded!");
    }
};
script.src = "file.js";
document.getElementsByTagName("head")[0].appendChild(script);
//hello

使用单一的方法动态加载JS文件,下面的函数封装了标准及IE特有的实现方法:

See the Pen onreadystatechange by whjin (@whjin) on CodePen.

动态脚本加载凭借它在跨浏览器兼容性和易用的优势,成为最通用的无阻塞加载解决方案。

XMLHTTPRequest脚本注入

另一种无阻塞加载脚本的方法是使用XHR对象获取脚本并注入页面中。此技术先创建一个XHR对象,然后用它下载JS文件,最后通过创建动态<script>元素将代码注入页面。

var xhr = new XMLHttpRequest();
xhr.open("get", "file.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.text = xhr.responseText;
            document.body.appendChild(script);
        }
    }
};

这种方法的主要局限性是JS文件必须与所请求的页面处于相同的域,所以JS文件不能从CDN下载。因此,大型Web应用通常不会采用XHR脚本注入技术。

推荐的无阻塞模式

向页面中添加大量JS的推荐做法只需两步:先添加动态加载所需的代码,然后加载初始化页面所需的剩下的代码。

举个例子:

<script src="loader.js"></script>
<script>
    loadScript("file.js", function () {
        Application.init();
    })
</script>

或者直接把函数直接嵌入页面,从而避免多产生一次HTTP请求。

YUI3的方式

使用DOM工具包,只需在YUI的use()方法中声明dom,并且提供回调函数即可:

YUI.use("dom", function (Y) {
    Y.DOM.addClass(document.body, "loaded");
});

LazyLoad类库

更为通用的延迟加载工具:LazyLoad。LazyLoad是loadScript()函数的增强版。

LazyLoad.js("file.js", function () {
    Application.init();
});

LazyLoad支持下载多个JS文件,并能保证在所有浏览器中都以正确的顺序执行。要加载多个JS文件,只需给LazyLoad.js()方法传入一个URL数组:

LazyLoad.js(["file1.js", "file2.js"], function () {
    Application.init();
});

LABjs

$LAB.script()方法用来定义需要下载的JS文件,$LAB.wait()用来指定文件下载并执行完毕后所调用的函数。LABjs鼓励链式调用,因此每个方法都会返回一个$LAB对象的引用。要下载多个JS文件,只需链式调用另一个$LAB.script()方法:

$LAB.script('file1.js')
    .script('file2.js')
    .wait(function () {
        Application.init();
    });

LABjs允许使用wait()方法来指定哪些文件需要等待其他文件,为确保第一个文件最先执行,必须在第一个script()方法后面调用wait()

小结

减少JS对性能的影响: