首页 > 代码库 > JavaScript非阻塞加载脚本

JavaScript非阻塞加载脚本

As more and more sites evolve into “Web 2.0″ apps, the amount of JavaScript increases. This is a performance concern because scripts have a negative impact on page performance. Mainstream browsers (i.e., IE 6 and 7)  block in two ways:

  • Resources in the page are blocked from downloading if they are below the script.
  • Elements are blocked from rendering if they are below the script.

随着越来越多的网站涉及“Web 2.0”应用,JavaScript脚本的数量也急剧增加。这是令人忧虑的,因为脚本对页面性能有负面影响。主流的浏览器(例如IE 6 、7)会以下面2种方式发生阻塞:

  • 如果资源位于脚本的下方,那么他们会被阻塞下载。泌阳县马奇建材
  • 如果元素位于脚本的下方,那么他们会被阻塞渲染展示。

The Scripts Block Downloads example demonstrates this. It contains two external scripts followed by an image, a stylesheet, and an iframe. The HTTP waterfall chart from loading this example in IE7 shows that the first script blocks all downloads, then the second script blocks all downloads, and finally the image, stylesheet, and iframe all download in parallel. Watching the page render, you’ll notice that the paragraph of text above the script renders immediately. However, the rest of the text in the HTML document is blocked from rendering until all the scripts are done loading.

这个脚本阻塞下载的例子(Scripts Block Downloads example) 展示了上面的情况。它包括2个外部脚本,后面是1个图片、1个样式表和1个iframe。使用IE7加载这个例子的HTTP瀑布图显示了第一个脚本阻塞了所有的下载,随后第2个脚本阻塞了所有的下载,最后图片样式表和iframe并行加载。观察页面的渲染情况,你会发现脚本上方的那段文字会马上渲染。然而,HTML 文档其它部分的文字会被阻塞,直到所有的脚本被下载完毕。

 

在 IE6 7, Firefox 2 3.0, Safari 3, Chrome 1, Opera 脚本均会阻塞下载

Browsers are single threaded, so it’s understandable that while a script is executing the browser is unable to start other downloads. But there’s no reason that while the script is downloading the browser can’t start downloading other resources. And that’s exactly what newer browsers, including Internet Explorer 8, Safari 4, and Chrome 2, have done. The HTTP waterfall chart for the Scripts Block Downloads example in IE8 shows the scripts do indeed download in parallel, and the stylesheet is included in that parallel download. But the image and iframe are still blocked. Safari 4 and Chrome 2 behave in a similar way. Parallel downloading improves, but is still not as much as it could be.

如果浏览器是单线程的,那么当一个脚本在执行时,浏览器不能下载其它资源是可以理解的。但是没有理由解释当脚本下载时浏览器不能下载其它资源。下载脚本的同时并行下载其它资源是所有最新浏览器的已经实现了的功能,包括Internet Explorer 8, Safari 4, Chrome 2。在IE8中打开脚本阻塞下载的例子(Scripts Block Downloads example)的HTTP瀑布图展示了脚本确实是并行下载的,样式表也是并行下载的,然而图片和iframe仍旧被阻塞了。Safari 4 和 Chrome 2 的行为也很类似。并行下载改善了一部分,但还没有达到它能达到的最好的效果。

 

IE8, Safari 4, Chrome 2下,脚本仍旧是阻塞的

Fortunately, there are ways to get scripts to download without blocking any other resources in the page, even in older browsers. Unfortunately, it’s up to the web developer to do the heavy lifting.

幸运的是,即使在更老的浏览器中,也有使脚本不阻塞其它页面资源的办法。不幸的是,这取决于web开发人员是否愿意承担这些繁重的工作。

There are six main techniques for downloading scripts without blocking:

  • XHR Eval – Download the script via XHR and eval() the responseText.
  • XHR Injection – Download the script via XHR and inject it into the page by creating a script element and setting its text property to the responseText.
  • Script in Iframe – Wrap your script in an HTML page and download it as an iframe.
  • Script DOM Element – Create a script element and set its src property to the script’s URL.
  • Script Defer – Add the script tag’s defer attribute. This used to only work in IE, but is now in Firefox 3.1.
  • document.write Script Tag – Write the <script src=""> HTML into the page using document.write. This only loads script without blocking in IE.

下面有6种非阻塞式加载脚本的技术:

  • XHR Eval - 通过XHR下载脚本,通过eval()执行。
  • XHR Injection - 通过XHR下载脚本,通过创建一个script的DOM元素注入页面,并且设置text属性。
  • Script in Iframe - 在一个HTML页面内包含脚本并通过iframe加载。
  • Script DOM Element - 创建一个script的DOM元素,并且设置src属性为脚本的url。
  • Script Defer - 增加脚本的defer属性。曾经仅在IE中可用,现在Firefox 3.1. 中也可以了。
  • document.write Script Tag - 在HTML页面内使用document.write <script src="">。仅在IE有效。

You can see an example of each technique using Cuzillion. It turns out that these techniques have several important differences, as shown in the following table. Most of them provide parallel downloads, although Script Defer and document.write Script Tag are mixed. Some of the techniques can’t be used on cross-site scripts, and some require slight modifications to your existing scripts to get them to work. An area of differentiation that’s not widely discussed is whether the technique triggers the browser’s busy indicators (status bar, progress bar, tab icon, and cursor). If you’re loading multiple scripts that depend on each other, you’ll need a technique that preserves execution order.

在Cuzillion你可以看到每个技术的样例。事实证明这些技术有重要的区别,见下面表格。它们大部分提供并行下载,有些技术不能被使用在跨域名的脚本上,有些必须在你已有的脚本基础上简单的修改,还有一个未被广泛讨论的是否触发浏览器的繁忙指示标记区别(状态栏,进度条,标签图标和光标)。如果你需要加载多个相互依存的脚本,你还需要一个维护执行顺序的技术。

技术名称支持并行下载支持跨域名不需修改脚本是否有繁忙指示保障执行顺序大小(字节)
XHR EvalIE, FF, Saf, Chr, OpnonoSaf, Chr-~500
XHR InjectionIE, FF, Saf, Chr, OpnoyesSaf, Chr-~500
Script in IframeIE, FF, Saf, Chr, OpnonoIE, FF, Saf, Chr-~50
Script DOM ElementIE, FF, Saf, Chr, OpyesyesFF, Saf, ChrFF, Op~200
Script DeferIE, Saf4, Chr2, FF3.1yesyesIE, FF, Saf, Chr, OpIE, FF, Saf, Chr, Op~50
document.write Script TagIE, Saf4, Chr2, OpyesyesIE, FF, Saf, Chr, OpIE, FF, Saf, Chr, Op~100

The question is: Which is the best technique? The optimal technique depends on your situation. This decision tree should be used as a guide. It’s not as complex as it looks. Only three variables determine the outcome: is the script on the same domain as the main page, is it necessary to preserve execution order, and should the busy indicators be triggered.

问题是:哪个是最好的技术?最佳的技术取决于您的具体情况。下面这个决策树可以作为一个指导。它看起来复杂,其实并不是。只有3个参数就决定了输出结果:脚本是否在主页面的同一个域名之下,是否需要保障执行顺序,是否需要触发繁忙指示标记。

 

Ideally, the logic in this decision tree would be encapsulated in popular HTML templating languages (PHP, Python, Perl, etc.) so that the web developer could just call a function and be assured that their script gets loaded using the optimal technique.

最理想的是,这个决策树的逻辑将体现在流行的HTML模板语言中(PHP, Python, Perl, 等),因此WEB开发人员可以简单的调用一个函数,就能保证他们的脚本使用最佳的技术来加载脚本。

In many situations, the Script DOM Element is a good choice. It works in all browsers, doesn’t have any cross-site scripting restrictions, is fairly simple to implement, and is well understood. The one catch is that it doesn’t preserve execution order across all browsers. If you have multiple scripts that depend on each other, you’ll need to concatenate them or use a different technique. If you have an inline script that depends on the external script, you’ll need to synchronize them. I call this “coupling” and present several ways to do this in Coupling Asynchronous Scripts.

很多情况下,使用 Script DOM Element是一个不错的选择。它可以在所有浏览器下工作,没有任何跨域限制,代码上很容易实现,很容易被理解,唯一不足的是并不是在所有浏览器下均能保持正确的执行顺序。如果你有多个脚本并且相互依赖,你需要合并他们或使用其它技术。如果你在页面内有依赖于外部脚本的内联脚本,你必须对他们实现同步化,我将这个方法称为“coupling” 并在耦合异步脚本中提出一些可以做到这一点的方法。

JavaScript非阻塞加载脚本