文章目录
  1. 1. 同步加载
  2. 2. 异步加载
    1. 2.1. Script DOM Element
    2. 2.2. onload 时的异步加载
    3. 2.3. Defer
    4. 2.4. async
    5. 2.5. XHR Eval
    6. 2.6. Script in Iframe
    7. 2.7. GMail Mobile
  3. 3. 延迟加载(lazy loading)
  4. 4. script 的两阶段加载 与 延迟执行(lazy execution)
  5. 5. JS最佳实践

默认情况javascript是同步加载的,也就是javascript的加载时阻塞的,后面的元素要等待javascript加载完毕后才能进行再加载,对于一些意义不是很大的javascript,如果放在页头会导致加载很慢的话,是会严重影响用户体验的。

同步加载

最常见加载方式:

<script src="http://yourdomain.com/script.js"></script> 

同步模式,又称阻塞模式,会阻止浏览器的后续处理,停止了后续的解析,因此停止了后续的文件加载(如图像)、渲染、代码执行。

js 之所以要同步执行,是因为 js 中可能有输出 document 内容、修改dom、重定向等行为,所以默认同步执行才是安全的。

以前的一般建议是把<script>放在页面末尾</body>之前,这样尽可能减少这种阻塞行为,而先让页面展示出来。

简单说:加载的网络 timeline 是瀑布模型,而异步加载的 timeline 是并发模型。

异步加载

异步加载又叫非阻塞,浏览器在下载执行 js 同时,还会继续进行后续页面的处理。

下面我们就介绍一些异步加载的方式:

Script DOM Element

(function() {
    var s = document.createElement('script');
    s.type = 'text/javascript';
    s.async = true;
    s.src = 'http://yourdomain.com/script.js';
    var x = document.getElementsByTagName('script')[0];
    x.parentNode.insertBefore(s, x);
})(); 

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

但是,这种加载方式在加载执行完之前会阻止 onload 事件的触发,而现在很多页面的代码都在 onload 时还要执行额外的渲染工作等,所以还是会阻塞部分页面的初始化处理。

onload 时的异步加载

(function() {
    function async_load(){
        var s = document.createElement('script');
        s.type = 'text/javascript';
        s.async = true;
        s.src = 'http://yourdomain.com/script.js';
        var x = document.getElementsByTagName('script')[0];
        x.parentNode.insertBefore(s, x);
    }
    if (window.attachEvent)
        window.attachEvent('onload', async_load);
    else
        window.addEventListener('load', async_load, false);
})();

这和前面的方式差不多,但关键是它不是立即开始异步加载 js ,而是在 onload 时才开始异步加载。这样就解决了阻塞 onload 事件触发的问题。

Defer

<script src="file.js" defer></script> 

defer 属性规定是否对脚本执行进行延迟,直到页面加载为止.

有的 javascript 脚本会使用 document.write 方法来创建当前的文档内容,如果您的脚本不会改变文档的内容,可将 defer 属性加入到 <script> 标签中,以便加快处理文档的速度。因为浏览器知道它将能够安全地读取文档的剩余部分而不用执行脚本,它将推迟对脚本的解释,直到文档已经显示给用户为止。

defer属性在IE 4.0中就实现了;Firefox 从 3.5 开始支持defer属性 。

所有的defer 脚本保证是按顺序依次执行的。

async

<script src="file.js" async></script> 

async属性是HTML5新增的。作用和defer类似,但是它将在下载后尽快执行,不能保证脚本会按顺序执行。它们将在onload 事件之前完成。

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

XHR Eval

通过 ajax 获取js的内容,然后 eval 执行。

var xhrObj = getXHRObject(); 
xhrObj.onreadystatechange =
function() {
    if ( xhrObj.readyState != 4 ) return;
    eval(xhrObj.responseText);
};
xhrObj.open('GET', 'A.js', true);
xhrObj.send(''); 

Script in Iframe

创建并插入一个iframe元素,让其异步执行 js 。

var iframe = document.createElement('iframe');
document.body.appendChild(iframe);
var doc = iframe.contentWindow.document;
doc.open().write('<body onload="insertJS()">');
doc.close(); 

GMail Mobile

页内 js 的内容被注释,所以不会执行,然后在需要的时候,获取script元素中 text 内容,去掉注释后 eval 执行。

<script type="text/javascript">
    /*
    var ...
    */
</script> 

延迟加载(lazy loading)

有些 js 代码并不是页面初始化的时候就立刻需要的,而稍后的某些情况才需要的。延迟加载就是一开始并不加载这些暂时不用的js,而是在需要的时候或稍后再通过js 的控制来异步加载。

也就是将 js 切分成许多模块,页面初始化时只加载需要立即执行的 js ,然后其它 js 的加载延迟到第一次需要用到的时候再加载。

特别是页面有大量不同的模块组成,很多可能暂时不用或根本就没用到。

就像图片的延迟加载,在图片出现在可视区域内时(在滚动条下拉)才加载显示图片。

script 的两阶段加载 与 延迟执行(lazy execution)

JS的加载其实是由两阶段组成:下载内容(download bytes)和执行(parse and execute)。

浏览器在下载完 js 的内容后就会立即对其解析和执行,不管是同步加载还是异步加载。

浏览器在解析执行 JS 阶段是阻塞任何操作的,这时的浏览器处于无响应状态。我 们都知道通过网络下载 script 需要明显的时间,但容易忽略了第二阶段,解析和执行也是需要时间的。script的解析和执行所花的时间比我们想象的要多,尤其是script 很多很大的时候。有些是需要立刻执行,而有些则不需要(比如只是在展示某个界面或执行某个操作时才需要)。

这些script 可以延迟执行,先异步下载缓存起来,但不立即执行,而是在第一次需要的时候执行一次。利用特殊的技巧可以做到 下载 与 执行的分离 比如将 JS 的内容作为 Image或 object 对象加载缓存起来,所以就不会立即执行了,然后在第一次需要的时候再执行。

JS最佳实践

  1. 最小化 js 文件,利用压缩工具将其最小化,同时开启http gzip压缩。工具:
  2. 尽量不要放在 中,尽量放在页面底部,最好是之前的位置
  3. 避免使用 document.write 方法
  4. 异步加载 js ,使用非阻塞方式,就是此文内容。
  5. 尽量不直接在页面元素上使用 Inline Javascript,如onClick 。有利于统一维护和缓存处理。
文章目录
  1. 1. 同步加载
  2. 2. 异步加载
    1. 2.1. Script DOM Element
    2. 2.2. onload 时的异步加载
    3. 2.3. Defer
    4. 2.4. async
    5. 2.5. XHR Eval
    6. 2.6. Script in Iframe
    7. 2.7. GMail Mobile
  3. 3. 延迟加载(lazy loading)
  4. 4. script 的两阶段加载 与 延迟执行(lazy execution)
  5. 5. JS最佳实践