设为首页收藏本站订阅更新

无忧脚本

 找回密码
 加入无忧

QQ登录

只需一步,快速开始

搜索
查看: 3782|回复: 0

jquery.autocomplete性能优化笔记

[复制链接]
发表于 2013-7-9 14:21:44 | 显示全部楼层 |阅读模式
本帖最后由 mutongmaijiu 于 2013-7-9 15:55 编辑

       最近有一个项目用到,jquery.autocomplete,本以为是一个之前项目组成员写的一个jquery插件,后来一搜索,不是内部原创,是引用一个广泛使用的公开插件。
       首先向作者致敬,作者解决了有无问题,本人侧重于结合项目做具体应用场景的性能优化。
       确认问题,上游反馈的用户体验糟糕的地方主要有两个,一个是搜索打开速度慢,一个是输入长关键词界面卡死现象。
       拿到代码,第一步确定性能瓶颈,粗略浏览代码,并没有发现一些恶名昭著的性能陷阱(复杂dom交互,大递归,大循环)初步确认可优化的项目有(标注为序号和蓝色)
              1、优化html模板系统,使用系统函数replace替代“+”
              2、合并html操作
              3、合并绑定事件
              4、使用[].join()代替+优化字符串操作
       代码引用形式为:
autoComplete({
                'target':'Dsearch',
                'input':'seachI',
                'container':'container',
                'url':'/search',
                'delay':100,
                'formatItem':function(item){
                        return '<span>'+item.name+'</span><em>'+item.count+'</em>';

                },【1】优化html模板【                'formatItem':function(item,i){
                        return '<li i={$i$}><span>{$name$}</span><em>{$count$}</em></li>'.replace("{$name$}",item.name).replace("{$count$}",item.count).replace("{$i$}",i);
                },】
                'result':function(item){
                        window.location=item.url;
                },
                'callback':function(){

                }
        });

核心代码为:
var autoComplete=function(params){
        var $target=$("#"+params.target);
        var $input=$("#"+params.input);
        var $container=$("#"+params.container);
        $input.attr('autocomplete','off');
        var doAutocomplete=function(){
                var oldv=$input.val();
                var autoAC=function(){
                        if(oldv!=$input.val()){
                                var temp=$.ajax({type:'post',url:params.url,data:{'key':$input.val()},dataType:"text",
                                        success: function(data){
                                           var json=eval("(" + data + ")");
                                                $container.html('');
                                                for(i=0;i<json.length;i++){
                                                        var $li=$("<li>"+params.formatItem(json)+"</li>");
                                                        $container.append($li);
                                                }【2】合并dom操作【4】使用[].join()代替+
                                               【优化后为】
                                            var str_html=[];
                                                for(i=0;i<json.length;i++){
                                                        str_html.push(params.formatItem(json,i));
                                                }
                                                $container.html(str_html.join(''));

                                                $container.find('li').each(function(i){
                                                        $(this).unbind('click');
                                                        $(this).bind('click',function(){
                                                                params.result(json);
                                                        })
                                                });【3合并事件绑定】
                                                【优化后为】
                                                var  mLi=$container.find('li');
                                                mLi.css("cursor","pointer");
                                                mLi.click(function(mEvent){
                                                            var i=mEvent.currentTarget.getAttribute("i");
                                                                params.result(json【i】);
                                                        });

                                                params.callback();
                                        }
                                });
                        }
                        oldv=$input.val();
                        
                        setTimeout(function(){
                                autoAC();
                        },params.delay);
                }
                autoAC();
        };
        doAutocomplete();
}

初步优化完成后,发现效果几乎没有,运行时间监测发现原来的代码执行时间为2-8毫秒,优化后虽然稳定在两毫秒之内,但对于数十秒的页面卡死面前,这做的工作几乎是无用的,技术上无明显缺陷,只能找程序设计策略的问题了,继续时间点监测各部分运行开销,最后初步确定系统开销最大为http请求,搜索关键字输入每一个关键字变动都向服务器提交http请求,输入一个六个拼音字母的词,将向服务器端提交六个请求,这样如果输入速度快的话,六个http请求瞬间就把浏览器卡死了。
最后偶然想起一个策略,销毁(终止)不必要的请求【题外:在此感谢一位263就职的老前辈,灵感来自与前辈的一次谈话】。结果一测试,效果比想象的也好。系统只保留最后一次http请求,早前的没有完成的在新请求之前终止。具体代码如下。紫红色标注
var autoComplete=function(params){
        var $target=$("#"+params.target);
        var $input=$("#"+params.input);
        var $container=$("#"+params.container);
        var currentAjaxs=[];
        $input.attr('autocomplete','off');
        var doAutocomplete=function(){
                var oldv=$input.val();
                var autoAC=function(){
                        if(oldv!=$input.val()){
                          var mAjax=$.ajax({type:'post',url:params.url,data:{'key':$input.val()},dataType:"text",
                                        success: function(data){
                                                var json=eval("(" + data + ")");
                                            var str_html=[];
                                                for(i=0;i<json.length;i++){
                                                        str_html.push(params.formatItem(json,i));
                                                }
                                                $container.html(str_html.join(''));
                                                var  mLi=$container.find('li');
                                                mLi.css("cursor","pointer");
                                                mLi.click(function(mEvent){
                                                            var i=mEvent.currentTarget.getAttribute("i");
                                                                params.result(json【i】);  //51js在线编辑器bug json【i】直接解析成斜体了,呵呵
          });
                                                params.callback();
                                        }
                                });
                                currentAjaxs.push(mAjax);
                                
                                if(currentAjaxs.length>2){
                     var tempA=currentAjaxs.splice(1,currentAjaxs.length-2);
                                     for(var j=1;j<tempA.length-2;j++){
                              tempA[j].abort();                                                  
                                                 }
                                       
                                         
                                        }
                        
                        }
                        oldv=$input.val();
                        
                        setTimeout(function(){
                                autoAC();
                        },params.delay);
                }
                autoAC();
        };
        doAutocomplete();
}
至此,jquery.autocomplete性能优化初步完成,效果为问题提出者接受,搜素无明显页面卡死情况。

本次优化主要涉及知识点有:(部分内容学自一位清华大学计算机系出来的牛人)
1、常见的性能陷阱 (复杂dom交互,大递归,大循环)当然更大的开销更大的陷阱是http往返
2、性能优化往往代码层面效果有限,需要更高层面策略考虑。
3、常见的函数层级性能优化 1)合并dom交互(多次变一次)2)[].join()优化字符串连接
4、性能优化 new Date().getMilliseconds()取得各段代码的执行时间,分析得到具体的运行瓶颈。
您需要登录后才可以回帖 登录 | 加入无忧

本版积分规则

小黑屋|手机版|Archiver|无忧脚本 ( 苏ICP备05080427号 )|值班电话:027-62300445   鄂公网安备 42011102000433号

GMT+8, 2017-11-23 19:07 , Processed in 0.092869 second(s), 8 queries , Gzip On, Memcache On.

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表