來自國外的頁面JavaScript文件優(yōu)化
The problem: scripts block downloads
Let's first take a look at what the problem is with the script downloads. The thing is that before fully downloading and parsing a script, the browser can't tell what's in it. It may contain document.write()
calls which modify the DOM tree or it may even contain location.href
and send the user to a whole new page. If that happens, any components downloaded from the previous page may never be needed. In order to avoid potentially useless downloads, browsers first download, parse and execute each script before moving on with the queue of other components waiting to be downloaded. As a result, any script on your page blocks the download process and that has a negative impact on your page loading speed.
Here's how the timeline looks like when downloading a slow JavaScript file (exaggerated to take 1 second). The script download (the third row in the image) blocks the two-by-two parallel downloads of the images that follow after the script:

Problem 2: number of downloads per hostname
Another thing to note in the timeline above is how the images following the script are downloaded two-by-two. This is because of the restriction of how many components can be downloaded in parallel. In IE <= 7 and Firefox 2, it's two components at a time (following the HTTP 1.1 specs), but both IE8 and FF3 increase the default to 6.
You can work around this limitation by using multiple domains to host your components, because the restriction is two components per hostname. For more information of this topic check the article “Maximizing Parallel Downloads in the Carpool Lane” by Tenni Theurer.
The important thing to note is that JavaScripts block downloads across all hostnames. In fact, in the example timeline above, the script is hosted on a different domain than the images, but it still blocks them.
Scripts at the bottom to improve user experience
Non-blocking scripts
It turns out that there is an easy solution to the download blocking problem: include scripts dynamically via DOM methods. How do you do that? Simply create a new <script>
element and append it to the <head>
:
var js = document.createElement('script');
js.src = 'myscript.js';
var head = document.getElementsByTagName('head')[0];
head.appendChild(js);
Here's the same test from above, modified to use the script node technique. Note that the third row in the image takes just as long to download, but the other resources on the page are loading simultaneously:
As you can see the script file no longer blocks the downloads and the browser starts fetching the other components in parallel. And the overall response time is cut in half.
Dependencies
A problem with including scripts dynamically would be satisfying the dependencies. Imagine you're downloading 3 scripts and three.js
requires a function from one.js
. How do you make sure this works?
Well, the simplest thing is to have only one file, this way not only avoiding the problem, but also improving performance by minimizing the number of HTTP requests (performance rule #1).
If you do need several files though, you can attach a listener to the script's onload
event (this will work in Firefox) and the onreadystatechange
event (this will work in IE). Here's a blog post that shows you how to do this. To be fully cross-browser compliant, you can do something else instead: just include a variable at the bottom of every script, as to signal “I'm ready”. This variable may very well be an array with elements for every script already included.
Using YUI Get utility
The YUI Get Utility makes it easy for you to use script includes. For example if you want to load 3 files, one.js
, two.js
and three.js
, you can simply do:
var urls = ['one.js', 'two.js', 'three.js'];
YAHOO.util.Get.script(urls);
YUI Get also helps you with satisfying dependencies, by loading the scripts in order and also by letting you pass an onSuccess
callback function which is executed when the last script is done loading. Similarly, you can pass an onFailure
function to handle cases where scripts fail to load.
var myHandler = {
onSuccess: function(){
alert(':))');
},
onFailure: function(){
alert(':((');
}
};
var urls = ['1.js', '2.js', '3.js'];
YAHOO.util.Get.script(urls, myHandler);
Again, note that YUI Get will request the scripts in sequence, one after the other. This way you don't download all the scripts in parallel, but still, the good part is that the scripts are not blocking the rest of the images and the other components on the page. .
YUI Get can also include stylesheets dynamically through the method YAHOO.util.Get.css()
[example].
Which brings us to the next question:
And what about stylesheets?
Stylesheets don't block downloads in IE, but they do in Firefox. Applying the same technique of dynamic inserts solves the problem. You can create dynamic link tags like this:
var h = document.getElementsByTagName('head')[0];
var link = document.createElement('link');
link.href = 'mycss.css';
link.type = 'text/css';
link.rel = 'stylesheet';
h.appendChild(link);
This will improve the loading time in Firefox significantly, while not affecting the loading time in IE.
Another positive side effect of the dynamic stylesheets (in FF) is that it helps with the progressive rendering. Usually both browsers will wait and show blank screen until the very last piece of stylesheet information is downloaded, and only then they'll start rendering. This behavior saves them the potential work of re-rendering when new stylesheet rules come down the wire. With dynamic <link>
s this is not happening in Firefox, it will render without waiting for all the styles and then re-render once they arrive. IE will behave as usual and wait.
But before you go ahead and implement dynamic <link>
tags, consider the violation of the rule of separation of concerns: your page formatting (CSS) will be dependent on behavior (JS). In addition, this problem is going to be addressed in future Firefox versions.
Other ways?
There are other ways to achieve the non-blocking scripts behavior, but they all have their drawbacks.
Method | Drawback |
---|---|
Using defer attribute of the script tag |
IE-only, unreliable even there |
Using document.write() to write a script tag |
|
XMLHttpRequest to get the source then execute with eval() . |
|
XHR request to get the source, create a new script tag and set its content |
|
Load script in an iframe |
|
Future
Safari and IE8 are already changing the way scripts are getting loaded. Their idea is to download the scripts in parallel, but execute them in the sequence they're found on the page. It's likely that one day this blocking problem will become negligible, because only a few users will be using IE7 or lower and FF3 or lower. Until then, a dynamic script tag is an easy way around the problem.
Summary
- Scripts block downloads in FF and IE browsers and this makes your pages load slower.
- An easy solution is to use dynamic
<script>
tags and prevent blocking. - YUI Get Utility makes it easier to do script and style includes and manage dependencies.
- You can use dynamic
<link>
tags too, but consider the separation of concerns first.
相關(guān)文章
使用JavaScript開發(fā)跨平臺(tái)的桌面應(yīng)用詳解
下面小編就為大家?guī)硪黄褂肑avaScript開發(fā)跨平臺(tái)的桌面應(yīng)用詳解。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-07-07javascript小數(shù)計(jì)算出現(xiàn)近似值的解決辦法
在javascript里面,小數(shù)只能進(jìn)行相似計(jì)算,例如:5.06+1.30,你得到的結(jié)果會(huì)是6.359999999999999,但有的小數(shù)計(jì)算又是正確的,如果計(jì)算出現(xiàn)了近似值,你可以用如下的方法計(jì)算。2010-02-02JS為什么說async/await是generator的語法糖詳解
這篇文章主要給大家介紹了關(guān)于JS為什么說async/await是generator的語法糖的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用JS具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-07-07JS 退出系統(tǒng)并跳轉(zhuǎn)到登錄界面的實(shí)現(xiàn)代碼
這篇文章介紹了退出系統(tǒng)后跳轉(zhuǎn)到登陸頁面的簡單JS代碼,有需要的朋友可以參考一下2013-06-06Javascript實(shí)現(xiàn)圖片輪播效果(一)讓圖片跳動(dòng)起來
圖片輪播效果,在各大網(wǎng)站的首頁都能看到,比較常見,下面腳本之家小編給大家介紹Javascript實(shí)現(xiàn)圖片輪播效果(一)讓圖片跳動(dòng)起來,需要的朋友參考下2016-02-02JavaScript該如何學(xué)習(xí) 怎樣輕松學(xué)習(xí)JavaScript
JavaScript該如何學(xué)習(xí)?如何輕松學(xué)習(xí)JavaScript?這篇文章主要介紹了輕松學(xué)習(xí)JavaScript的方法2017-06-06JS面向?qū)ο螅?)之Object類,靜態(tài)屬性,閉包,私有屬性, call和apply的使用,繼承的三種實(shí)現(xiàn)方法
這篇文章主要介紹了JS面向?qū)ο螅?)之Object類,靜態(tài)屬性,閉包,私有屬性, call和apply的使用,繼承的三種實(shí)現(xiàn)方法的相關(guān)資料,需要的朋友可以參考下2016-02-02JavaScript 獲取當(dāng)前日期時(shí)間 年月日 時(shí)分秒的方法
這篇文章主要介紹了JavaScript 獲取當(dāng)前日期時(shí)間年月日時(shí)分秒的方法,通過案例代碼介紹了獲取當(dāng)前日期方法,代碼簡單易懂,需要的朋友可以參考下2023-10-10js獲取select默認(rèn)選中的Option并不是當(dāng)前選中值
這篇文章主要介紹了js如何獲取select默認(rèn)選中的Option并不是當(dāng)前選中的值,需要的朋友可以參考下2014-05-05js實(shí)現(xiàn)把圖片的絕對路徑轉(zhuǎn)為base64字符串、blob對象再上傳
本文主要介紹了JavaScript把項(xiàng)目本地的圖片或者圖片的絕對路徑轉(zhuǎn)為base64字符串、blob對象再上傳的方法,具有一定的參考價(jià)值,需要的朋友一起來看下吧2016-12-12