手机浏览 RSS 2.0 订阅 膘叔的简单人生 , 腾讯云RDS购买 | 超便宜的Vultr , 注册 | 登陆
浏览模式: 标准 | 列表全部文章

【转】jQuery性能规则

转载的关于jQuery性能设置的文章 。。。

 之前,我们需要担心减少字节数和请求次数以及加载顺序以使页面加载的更快。如今,我们越来越多的注意到一个影响性能的重要组成部分--CPU利用 率。使用 jQuery和其他JavaScript框架,使节点选择和DOM操作变得越来越容易,但如果你不小心使用了一些减少浏览器本身要做的工作的一些做法可能 会带来不好的结果。

  1. 总是使用#id去寻找element
  2. 在Classes前面使用Tags
  3. 缓存jQuery对象
  4. 更好的利用链
  5. 使用子查询
  6. 限制直接对DOM操作
  7. 事件委托(又名:冒泡事件)
  8. 消除查询浪费
  9. 遵从 $(window).load
  10. 压缩JS
  11. 学习jQuery库

1.总是使用#id去寻找element.

 在jQuery中最快的选择器是ID选择器 ($('#someid')). 这是因为它直接映射为JavaScript的getElementById()方法。

选择单个元素

XML/HTML代码
  1. <div id="content">  
  2.     <form method="post" action="/">  
  3.         <h2>Traffic Light</h2>  
  4.         <ul id="traffic_light">  
  5.             <li><input type="radio" class="on" name="light" value="red" /> Red</li>  
  6.             <li><input type="radio" class="off" name="light" value="yellow" /> Yellow</li>  
  7.             <li><input type="radio" class="off" name="light" value="green" /> Green</li>  
  8.         </ul>  
  9.         <input class="button" id="traffic_button" type="submit" value="Go" />  
  10.     </form>  
  11. </div>  

选择button的性能不好的一种方式:
var traffic_button = $('#content .button');

取而代之的是直接选择button:
var traffic_button = $('#traffic_button');

选择多个元素

在我们讨论选择多个元素的时候,我们真正需要知道的是DOM的遍历和循环才是性能低下的原因。为了尽量减少性能损失, 总是使用最近的父ID去寻找。
var traffic_lights = $('#traffic_light input');

2.在Classes前面使用Tags

在jQuery中第二快的选择器就是Tag选择器 ($('head')). 而这是因为它直接映射到JavaScript的getElementsByTagName()方法。

XML/HTML代码
  1. <div id="content">  
  2.     <form method="post" action="/">  
  3.         <h2>Traffic Light</h2>  
  4.         <ul id="traffic_light">  
  5.             <li><input type="radio" class="on" name="light" value="red" /> Red</li>  
  6.             <li><input type="radio" class="off" name="light" value="yellow" /> Yellow</li>  
  7.             <li><input type="radio" class="off" name="light" value="green" /> Green</li>  
  8.         </ul>  
  9.         <input class="button" id="traffic_button" type="submit" value="Go" />  
  10.     </form>  
  11. </div>  

总是在一个Class前面加上一个tag名字(记得从一个ID传下来)
var active_light = $('#traffic_light input.on');

注意:在jQuery里Class选择器是最慢的一个选择器;在IE中它循环整个DOM。可能的话尽量避免使用它。不要在ID前面加Tags。例如,它会因为去循环所有的<div>元素去寻找ID为content的<div>,而导致很慢。
var content = $('div#content');

按照同样的思路,从多个ID传下来是冗余的。
var traffic_light = $('#content #traffic_light');

3.缓存jQuery对象

养成保存jQuery对象到一个变量上(就像上面的例子)的习惯。例如,不要这样做:

JavaScript代码
  1. $('#traffic_light input.on).bind('click', function(){...}); 
  2. $('#traffic_light input.on).css('border''3px dashed yellow');  
  3. $('#traffic_light input.on).css('background-color', 'orange'); 
  4. $('#traffic_light input.on).fadeIn('slow');  

取而代之,首现保存jQuery变量到一个本地变量后,再继续你的操作。

JavaScript代码
  1. var $active_light = $('#traffic_light input.on');  
  2. $active_light.bind('click'function(){...});  
  3. $active_light.css('border''3px dashed yellow');  
  4. $active_light.css('background-color''orange');  
  5. $active_light.fadeIn('slow');  

提示:使用$前辍表示我们的本地变量是一个jQuery包集。记住,不要在你的应该程序里出现一次以上的jQuery重复的选择操作。

 额外提示:延迟存储jQuery对象结果。

如果你想在你的程序的其它地方使用jQuery结果对象(result object(s)),或者你的函数要执行多次,要把它缓存在一个全局范围的对象里。通过定义一个全局容器保存jQuery结果对象,就可以在其它的函数里引用它。

JavaScript代码
  1. // Define an object in the global scope (i.e. the window object)  
  2. window.$my =  
  3. {  
  4.     // Initialize all the queries you want to use more than once  
  5.     head : $('head'),  
  6.     traffic_light : $('#traffic_light'),  
  7.     traffic_button : $('#traffic_button')  
  8. };  
  9.   
  10. function do_something()  
  11. {  
  12.     // Now you can reference the stored results and manipulate them  
  13.     var script = document.createElement('script');  
  14.     $my.head.append(script);  
  15.   
  16.     // When working inside functions, continue to save jQuery results  
  17.     // to your global container.  
  18.     $my.cool_results = $('#some_ul li');  
  19.     $my.other_results = $('#some_table td');  
  20.   
  21.     // Use the global functions as you would a normal jQuery result  
  22.     $my.other_results.css('border-color''red');  
  23.     $my.traffic_light.css('border-color''green');  
  24. }  

4.更好的利用链

 前面的例子也可以这样写:

JavaScript代码
  1. var $active_light = $('#traffic_light input.on');$active_light.bind('click'function(){...})  
  2.     .css('border''3px dashed yellow')  
  3.     .css('background-color''orange')  
  4.     .fadeIn('slow');  

这样可以让我们写更少的代码,使JavaScript更轻量。

5.使用子查询

jQuery允许我们在一个包集上附加其它的选择器。因为我们已经在本地变量里保存了父对象这样会减少以后在选择器上的性能开销。

XML/HTML代码
  1. <div id="content">  
  2.     <form method="post" action="/">  
  3.         <h2>Traffic Light</h2>  
  4.         <ul id="traffic_light">  
  5.             <li><input type="radio" class="on" name="light" value="red" /> Red</li>  
  6.             <li><input type="radio" class="off" name="light" value="yellow" /> Yellow</li>  
  7.             <li><input type="radio" class="off" name="light" value="green" /> Green</li>  
  8.         </ul>  
  9.         <input class="button" id="traffic_button" type="submit" value="Go" />  
  10.     </form>  
  11. </div>  

例如,我们可以利用子查询缓存active和inactive lights以便后面的操作。

JavaScript代码
  1. var $traffic_light = $('#traffic_light'),  
  2.     $active_light = $traffic_light.find('input.on'),  
  3.     $inactive_lights = $traffic_light.find('input.off');  

提示:可以用逗号隔开一次定义多个本地变量,这样可以节省一些字节。

 6.限制直接对DOM操作

DOM操作的基本做法是在内存中创建DOM结构,然后再更新DOM结构。这不是jQuery最好的做法,但对JavaScript来讲是高效的。直接操作DOM结构性能是低下的。 例如,如果你需要动态创建一列元素,不要这样做:

JavaScript代码
  1. var top_100_list = [...], // assume this has 100 unique strings  
  2.     $mylist = $('#mylist'); // jQuery selects our <ul> element  
  3.   
  4. for (var i=0, l=top_100_list.length; i<l; i++)  
  5. {  
  6.     $mylist.append('<li>' + top_100_list[i] + '</li>');  
  7. }  

取而代之,我们希望在插入DOM结构之前先在一个字符串里创建一套元素。

JavaScript代码
  1. var top_100_list = [...], // assume this has 100 unique strings  
  2.     $mylist = $('#mylist'), // jQuery selects our <ul> element  
  3.     top_100_li = ""// This will store our list items  
  4.   
  5. for (var i=0, l=top_100_list.length; i<l; i++)  
  6. {  
  7.     top_100_li += '<li>' + top_100_list[i] + '</li>';  
  8. }  
  9. $mylist.html(top_100_li);  

更快的做法,在插入DOM结构之前我们应该总是在一个父节点里包含许多元素

JavaScript代码
  1. var top_100_list = [...], // assume this has 100 unique strings  
  2.     $mylist = $('#mylist'), // jQuery selects our <ul> element  
  3.     top_100_ul = '<ul id="#mylist">'// This will store our entire unordered list  
  4.   
  5. for (var i=0, l=top_100_list.length; i<l; i++)  
  6. {  
  7.     top_100_ul += '<li>' + top_100_list[i] + '</li>';  
  8. }  
  9. top_100_ul += '</ul>'// Close our unordered list  
  10.   
  11. $mylist.replaceWith(top_100_ul);  

如是你照着上面的做了还是对性能有些迷惑的话,可以参考以下内容:

7. 事件委托(又名:冒泡事件)

除非特别说明,每一个JavaScript事件(如click, mouseover 等)在DOM结构树上都会冒泡到它的父元素上。如果我们想让很多elements(nodes)调用同一个function这是非常有用的。取而代之的是 你可以只对它们的父级绑定一次,而且可以计算出是哪一个节点触发了事件,而不是绑定一个事件监听器到很多节点上这种效率低下的方式。例如,假如我们要开发 一个包含很多input的大型form,当input被选择的时候我们想绑定一个class name。像这样的帮定是效率低下的:

XML/HTML代码
  1. $('#myList li).bind('click', function(){  
  2.     $(this).addClass('clicked');  
  3.     // do stuff  
  4. });  

 

反而,我们应该在父级侦听click事件。

JavaScript代码
  1. $('#myList).bind('click', function(e){ 
  2.     var target = e.target, // e.target grabs the node that triggered the event. 
  3.         $target = $(target);  // wraps the node in a jQuery object 
  4.     if (target.nodeName === 'LI') {  
  5.         $target.addClass('clicked');  
  6.         // do stuff  
  7.     }  
  8. });  

父节点担当着发报机的工作,可以在触发了事件的目标element上做一些工作。如果你发现自己把一个event listener帮定到很多个element上,那么你这种做法是不正确的。

 8.消除查询浪费

虽然jQuery对没有找到任何匹配的elements处理的很好,但是它还是需要花费时间去查找的。如果你的站点有一个全局的JavaScript,你可能会把每个jQuery function都放在 $(document).ready(function(){ // all my glorious code })里。 不要这样做。只去放一些页面上适合用到的function。这样做最有效的方式是你的模板可以完全控制任何时候或者地方执行JavaScript以内联脚 本的方式初始化function。例如,在你的“article”页面模板里,你可能在body标签关闭之前包含以下代码

XML/HTML代码
  1. <script type="text/javascript>  
  2. mylib.article.init();  
  3. </script>  
  4. </body>  

如果你的页面模板包含多种有可能在页面或者不在页面上的模块,或者为了可视化效果你需要它们稍后再初如化,你应该在这些模块之后立即放置初如化函数。

XML/HTML代码
  1. <ul id="traffic_light">  
  2.     <li><input type="radio" class="on" name="light" value="red" /> Red</li>  
  3.     <li><input type="radio" class="off" name="light" value="yellow" /> Yellow</li>  
  4.     <li><input type="radio" class="off" name="light" value="green" /> Green</li>  
  5. </ul>  
  6. <script type="text/javascript>  
  7. mylib.traffic_light.init();  
  8. </script>  
你的全局JavaScript库看起来应该是这样的:
JavaScript代码
  1. var mylib =  
  2. {  
  3.     article_page :  
  4.     {  
  5.         init : function()  
  6.         {  
  7.             // Article page specific jQuery functions.  
  8.         }  
  9.     },  
  10.     traffic_light :  
  11.     {  
  12.         init : function()  
  13.         {  
  14.             // Traffic light specific jQuery functions.  
  15.         }  
  16.     }  
  17. }  

9 遵从$(windows).load

有一种诱惑会使jQuery开发者hook所有事情到 $(document).ready 这个虚伪的事件里。毕竟在大多数例子里都可以看到这样使用。虽然$(document).ready 非常有用,它在页面呈现时发生,虽然其它对象还在下载中。如果你发现你的页面在下载中停顿,就有可能是$(document).ready 引起的。你可以通过把jQuery functions帮定到$(window).load事件来减少下面下载时的CPU使用率,它是在所有HTML(包括iframe内容)都下载完以后才去调用所有对象的。

$(window).load(function(){
    
// jQuery functions to initialize after the page has loaded.
});

多余的功能,如拖拽、帮定可视化效果和动画、预读取图片等,使用这种方法比较好。

10 压缩JS

虽然和jQuery无关,但在这里也要提一下。使JavaScript函数和变量变得可读是一个趋势,这对开发者来讲是必不可少的,但对普通用户来 讲没有任何关系。不用什么借口,是时候把JS压缩纳入我们的工作流程中来了。注释你的代码,在投放到生产环境之前找一个压缩工具进行压缩。使用YUICompressor 压缩你代码中多余的浪费的字节。根据我们的经验,它可以安全的把JavaScript压缩的尽可能小,而不会多占用CPU。小提示:为了在YUICompressor里最大化压缩,应该这样这样定义变量(例如:var my_long_variable_name;)

11 学习jQuery库

学习jQuery最好的方法就是去查jQuery的文档了,可以当作手册来用。

本文来自博客园,作者是Skyline.xin,来源网址是:http://www.cnblogs.com/trendline/archive/2010/02/05/jquery-performance-rules.html,用他的话来说,他是翻译:http://www.artzstudio.com/2009/04/jquery-performance-rules/,可惜我的英文太差。否则我也可以翻译啥的了。。

感谢翻译者为我们带来的快乐。

一次糟糕的测试

关于飞信,我上次写过学着写PHP飞信,然而今天晚上我把上文中测试的内容重现时却发现,现实有点残酷。我用curl的时候,速度居然没有fsocketopen快?我于是多次测试,然而,我却得到了一些令人沮丧的结果

【$postData,$postLength,都不提供。。。】

1、curl,利用curl来post

平均【大约 1.8~1.9秒】
  1. $ch = curl_init();  
  2. $chOptions = array(  
  3.     CURLOPT_URL => "http://nav.fetion.com.cn/nav/getsystemconfig.aspx",  
  4.     CURLOPT_HTTP_VERSION => 'HTTP/1.0',  
  5.     CURLOPT_USERAGENT => sprintf('IIC2.0/pc %s' , self::FETION_CLIENT_VERSION),  
  6.     CURLOPT_ENCODING => 'gzip,deflate',  
  7.     CURLOPT_HTTPHEADER => array('Content-Type: text/xml; charset=utf-8'),  
  8.     CURLOPT_POST => 1,  
  9.     CURLOPT_POSTFIELDS => $postData ,  
  10.     CURLOPT_RETURNTRANSFER => 1,  
  11.     CURLOPT_HEADER => 0  
  12. );  
  13. curl_setopt_array($ch$chOptions);  
  14. $res = curl_exec($ch);  

 2、fsocketopen,同样POST数据

平均【大约 0.8~0.9秒】
  1. $headers=<<<eot  
  2. POST /nav/getsystemconfig.aspx HTTP/1.0  
  3. User-Agent: IIC2.0/pc 3.1.0480  
  4. Accept-Encoding: deflate, gzip  
  5. Host: nav.fetion.com.cn:80  
  6. Content-Length: {$postLength}  
  7.   
  8. {$postData}  
  9. eot;  
  10. fwrite($fp,$headers);  
  11. $received=fread($fp,1024);  
  12. fclose($fp);  

3、stream,利用文件流

平均【大约 1.3~1.4秒】
  1. $opts = array (  
  2.     'http' => array (  
  3.         'method' => 'POST',  
  4.         'header'=> "Content-type: application/x-www-form-urlencoded" .  
  5.                    "Content-Length: " . $postLength . "",  
  6.         'content' => $postData  
  7.     ),  
  8. );  
  9. $context = stream_context_create($opts);  
  10. //$received = file_get_contents('http://nav.fetion.com.cn/nav/getsystemconfig.aspx', false, $context , 0 ,1024);  
  11. $fp = fopen('http://nav.fetion.com.cn/nav/getsystemconfig.aspx','r',false,$context);  
  12. if($fp){  
  13.     $received = fgets($fp, 512);  //在这里我用了三种测试,fgets,fread,stream_get_contents
  14.     fclose($fp);  
  15. }  

在这里我用了两种方式,一种是file_get_contents还有一种是fopen,file_get_contents的方法被我注释了。。。

在这三种中,我发现真的只有fsocketopen后然后fread是最快的。

真让我沮丧。。因为第三种方法中file_get_contens也可以指定读取的长度。但PHPRPC作者andot却认为,file_get_contents把头已经读回来了。我想,file_get_contents其实已经把数据读回来了,虽然是指定了长度,但其实只是读回来后再截取的。

唉。看来,不得不用fsocketopen了。。不知道除此之外有没有什么好办法。

海报

昨天是老婆生日,陪着她去逛了逛 商场,在她逛的时候,我就在那里偷拍商场的海报。
拍的时候还是很当心的,怕被人痛欧。没办法,这年头,都是喜欢拿着鸡毛当令箭。

有的海报我还是挺喜欢的,象E.LAND品牌的,看起来就象电影海报。还有一些,也挺漂亮,只是海报居然对着商铺的里侧,不走进去还真看不到,这样又怎么体现出海报的作用?真想不通。

或者最近一直在想着如何赢利,所以看到一些特别的东西就想着记录下来。昨天还拍了一张曹扬的“创业小店”,说是团委搞的一个创业园区,说白了就是让大学生自己养活自己而己。唉。。

上图【晚上,没办法。。有点糊了】
大小: 1.46 M
尺寸: 500 x 375
浏览: 1475 次
点击打开新窗口浏览全图

哪天自己能够创业?想起一哥们,已经陷在创业的泥潭。我胆量还是挺小的。
老婆说,郑渊洁有次在采访的时候说,如果在法律允许的范围内,越早有小孩是越好的,因为只有那样你才有压力和动力去创业。
现在,却是想着要稳定,却不知道怎么搞

对了,我说的海报的图在:http://g.52cd.net/archives/356,点击小图可以看大图。

学着写PHP飞信

飞信,对于我们搞开发的人来说,确实还是需要的。网上也有一些php飞信的类(看的也很累),不过,我看的是zendstudio.net,因为看起来好象方便。
自从上次飞信改过IP地址后,我才真正的仔细看过这方面的东西(其实也只是稍看看啦。。。哈哈)

其实,说真的,飞信这东西,PHP版的在我眼里感觉很深奥,因为要抓包,以前还从来没有这样抓过,总觉得是一件很复杂很难的事情。但自从不能给好友发短信后,我到Zendstudio.net上看过别人回复后,才发现他根本就没有提供。所以就萌发了要自己的写的念头。

于是找了smartsniff这个抓包工具,找来libfetion,关掉我所有的上网的程序。然后运行他开始抓包。
发现真的很方便。。。
于是就准备开始写了。不用fsockopen,而准备直接采用curl。。。目前刚刚写了一个getSIPC,发现速度好象是比fsockopen的速度快很多。【可能是因为,zendstudio.net他是按字节读取,然后用正则处理获得sipcproxy,而我是直接取回来用simplexml_loadstring,然后直接输出这个对象的值 ?也或许只是一个感觉而己,其实并没有变化?晚上回去输出执行时间看一下就知道了。HOHO】

慢慢写,因为注意过给好友发短信的功能,是利用SID的,但普通情况下,获取不到SID,必须得先把好友拉回来做一个缓存。这样就可以了。所以本版飞信应该不会公开,否则,谁也不敢使用。【随便说说,还没写完呢。。。。】

Tags: 飞信, fetion, php

最近一些想做和正在做的事的记录

快过年了。一些在做和想做的事情需要记录一下了

1、kenai.com要关门了。sbPHP没有地方放源码了。只能天天晚上更新了,不过正好又准备重写一下,所以问题不大,影响也小

2、kkread.com,原来想做小说站的。现在改成代码快读,专门用来研究php,js以及wordpress。等数据多一点后,考虑的是提供整合下载,代码压缩等。

3、mzditu.com,是一直想做的,但没有时间做的。在playgoogle.com上也看到一些操作google map的代码,但却一直没有动手过。。。

4、wordpress学习以及插件研究一下。毕竟,我不是纯粹用来玩的。

5、想好好读读优秀代码,想好好看看书

6、学python,NS团队的hihiyou在博客上贴了点用python做的编辑器,吸引了我。。。

7、想好好看看JS,多写一些jQuery插件。