来自国外的页面JavaScript文件优化


Posted in Javascript onDecember 08, 2010

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:

来自国外的页面JavaScript文件优化

Here's the example to test yourself.

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

As Yahoo!'s Performance rules advise, you should put the scripts at the bottom of the page, towards the closing </body> tag. This doesn't really make the page load faster (the script still has to load), but helps with the progressive rendering of the page. The user perception is that the page is faster when they can see a visual feedback that there is progress.

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:

来自国外的页面JavaScript文件优化

Test example

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. Here's a good example and tutorial on using YUI Get to load scripts.

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 Non-blocking behavior is in IE-only document.write is not a recommended coding practice
XMLHttpRequest to get the source then execute with eval(). “eval() is evil” same-domain policy restriction
XHR request to get the source, create a new script tag and set its content more complex same-domain policy
Load script in an iframe complex iframe overhead same-domain policy

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.
Javascript 相关文章推荐
jQuery 加上最后自己的验证
Nov 04 Javascript
node.js WEB开发中图片验证码的实现方法
Jun 03 Javascript
基于jquery实现图片相关操作(重绘、获取尺寸、调整大小、缩放)
Dec 25 Javascript
JavaScript调试的多个必备小Tips
Jan 15 Javascript
javascript 显示全局变量与隐式全局变量的区别
Feb 09 Javascript
angular中ui calendar的一些使用心得(推荐)
Nov 03 Javascript
Vue实现动态创建和删除数据的方法
Mar 17 Javascript
用jQuery将JavaScript对象转换为querystring查询字符串的方法
Nov 12 jQuery
layui lay-verify form表单自定义验证规则详解
Sep 18 Javascript
JS操作字符串转数字的常见方法示例
Oct 29 Javascript
探索node之事件循环的实现
Oct 30 Javascript
vue element和nuxt的使用技巧分享
Jan 14 Vue.js
js 替换功能函数,用正则表达式解决,js的全部替换
Dec 08 #Javascript
javascript中callee与caller的用法和应用场景
Dec 08 #Javascript
js下通过prototype扩展实现indexOf的代码
Dec 08 #Javascript
在JQuery dialog里的服务器控件 事件失效问题
Dec 08 #Javascript
jquery蒙版控件实现代码
Dec 08 #Javascript
基于JQuery制作的产品广告效果
Dec 08 #Javascript
关于用Jquery的height()、width()计算动态插入的IMG标签的宽高的问题
Dec 08 #Javascript
You might like
浅析PHP安装扩展mcrypt以及相关依赖项(PHP安装PECL扩展的方法)
2013/07/05 PHP
php中OR与|| AND与&amp;&amp;的区别总结
2013/10/26 PHP
PHP数组遍历知识汇总(包含遍历方法、数组指针操作函数、数组遍历测速)
2014/07/05 PHP
WebQQ最新登陆协议的用法
2014/12/22 PHP
WordPress特定文章对搜索引擎隐藏或只允许搜索引擎查看
2015/12/31 PHP
Thinkphp结合AJAX长轮询实现PC与APP推送详解
2017/07/31 PHP
使用PHP反射机制来构造&quot;CREATE TABLE&quot;的sql语句
2019/03/21 PHP
thinkphp框架使用JWTtoken的方法详解
2019/10/10 PHP
js获取提交的字符串的字节数
2009/02/09 Javascript
jQuery 自动增长的文本输入框实现代码
2010/04/02 Javascript
JS获得浏览器版本和操作系统版本的例子
2014/05/13 Javascript
兼容最新firefox、chrome和IE的javascript图片预览实现代码
2014/08/08 Javascript
基于javascript制作微信聊天面板
2020/08/09 Javascript
JS常用正则表达式总结【经典】
2017/05/12 Javascript
Angular 通过注入 $location 获取与修改当前页面URL的实例
2017/05/31 Javascript
如何开发出更好的JavaScript模块
2017/12/22 Javascript
Vue组件系列开发之模态框
2019/04/18 Javascript
vue.js实现双击放大预览功能
2020/06/23 Javascript
vue 清空input标签 中file的值操作
2020/07/21 Javascript
ant design vue中日期选择框混合时间选择器的用法说明
2020/10/27 Javascript
[50:44]DOTA2-DPC中国联赛 正赛 SAG vs Dragon BO3 第二场 2月22日
2021/03/11 DOTA
Python中的ConfigParser模块使用详解
2015/05/04 Python
详解Numpy数组转置的三种方法T、transpose、swapaxes
2019/05/27 Python
梅尔倒谱系数(MFCC)实现
2019/06/19 Python
pytorch 数据处理:定义自己的数据集合实例
2019/12/31 Python
PyCharm 2020.2 安装详细教程
2020/09/25 Python
Laura官网:加拿大女性的顶级时尚目的地
2019/09/20 全球购物
Pandora西班牙官方商店:PandoraShop.es
2020/10/05 全球购物
2014年社区妇联工作总结
2014/12/02 职场文书
2014年工作总结及2015工作计划
2014/12/12 职场文书
办公室岗位职责范本
2015/04/11 职场文书
保外就医申请书范文
2015/08/06 职场文书
创业计划书之情侣餐厅
2019/09/29 职场文书
励志语录:只有自己足够强大,才能不被别人践踏
2020/01/09 职场文书
苹果M1芯片安装nginx 并且部署vue项目步骤详解
2021/11/20 Servers
Python实现Hash算法
2022/03/18 Python