【问题标题】:is my jQuery script performing bad?我的 jQuery 脚本执行不好?
【发布时间】:2009-07-28 08:35:25
【问题描述】:

我为照相馆构建了一个 Web 工具,以启动由打印机处理的打印订单。

在较旧的 Mac 上,我的脚本性能不佳。 我想知道我的脚本中是否有一些性能不佳的部分。

你们看到这么低性能的部件吗?

谢谢,马克斯

$(function() {

/* when start button is clicked */
$('.start_btn').click(function() {

    /* remove all previous input fields */
    $("#dialog_fieldset").children().remove();

    /* save id */
    id = $(this).attr('name');

    /* get data from db */
    $.post("inc/get_start_order_infos.inc.php", { id: id },

        /* Callback */
        function(data){

            /* make data globally available */
            daten = data;

            /* build the var 'infos_html' */
            var infos_html = '<label for="kunden_nr">Kunden-Nr.</label><input type="text" name="kunden_nr" id="kunden_nr" ';
            infos_html += 'value="' + data.kunden_nr + '" class="text ui-widget-content ui-corner-all" />';
            infos_html += '<label for="sort">Sort</label><input type="text" name="sort" id="sort" value="';
            infos_html += data.sort_nr + '" class="text ui-widget-content ui-corner-all" />';

            /* append infos_html to the fieldset */
            $('#dialog_fieldset').append(infos_html);

            /* Code Index */
            anzahlCodes = 1;

            /* For each element within the sub array codes */
            for(e in data.codes){

                /* build the var 'code_html' */
                var code_html = '<label for="code' + anzahlCodes + '">Code ' + anzahlCodes;
                code_html += '</label><input type="text" name="code' + anzahlCodes + '" id="code' + anzahlCodes;
                code_html += '" value="' + data.codes[e] + '" class="text ui-widget-content ui-corner-all" />';

                /* append code_html to the fieldset */
                $('#dialog_fieldset').append(code_html);
                anzahlCodes++;
            }

            $('#dialog').dialog('open');
            $('#kunden_nr').select();

    }, "json");

    return false;
});

$("#dialog").dialog({
    bgiframe: false,
    autoOpen: false,
    height: 350,
    modal: true,
    buttons: {
        'Auftrag starten': function() {

            /* close dialog */
            $(this).dialog('close');

            /* create the info array to be submitted */
            var arr_infos = new Array();
            arr_infos[0] = id;
            arr_infos[1] = $('#kunden_nr').attr('value');
            arr_infos[2] = $('#sort').attr('value');

            /* write inputs into the str_codes */
            var str_codes = "";
            for (var i=1; i < anzahlCodes; i++){
                var cde = '#code' + i;
                if(i == 1){
                    str_codes += $(cde).attr('value');
                }
                else{
                    str_codes += "<--->" + $(cde).attr('value');
                }
            }


            /* execute start */
            $.post("inc/start_orders.inc.php", { 'corrected_infos[]':arr_infos, 'corrected_codes': str_codes },

                /* Callback */
                function(data){
                    $('#notice').remove();

                    /* if start was successful */
                    if (data.mstype == 'success'){

                        /* the pressed button */
                        btn = ".start_btn[name=" + id + "]";
                        /* the tr where the button is inside */
                        tr = $(btn).parent().parent();
                        /* remove red */
                        $(tr).removeClass('rot');

                        /* set text of Kunden-Nr. td */
                        $(tr).children().eq(3).text(arr_infos[1]);

                        /* set text of Sort td */
                        $(tr).children().eq(4).text(arr_infos[2]);

                        /* set text of Code td */
                        $(tr).children().eq(5).text(str_codes);

                        /* set text of start td */
                        $(tr).children().eq(8).text('ja');

                        /* prepend notice */
                        $("#outline").prepend("<div id='notice'>" + data.ms + "</div>");
                    }

                    /* if not successful */
                    else {
                        $("#outline").prepend("<div id='notice' class='notice_err'>" + data.ms + "</div>");
                    }
                }, "json");
        },
        'Abbrechen': function() {
            $(this).dialog('close');
        }
    }
});

【问题讨论】:

    标签: jquery performance


    【解决方案1】:

    我的建议在 //++ 指示的 cmets 代码中。通常,当您尽可能少地搜索或更改 DOM 时,JavaScript 会更快。

    主要关注点在于您的循环和您对$(tr).的重复使用

    我没有测试过这段代码。

    $(function() {
        //++ jQuery doesn't cache this call, so it has to search the DOM each time.  
        //++ I've moved any node that is requested more than once here
        var dialog_fieldset_node = $("#dialog_fieldset"),
            dialog_node          = $("#dialog"),
            kunden_nr_node       = $("#kunden_nr"),
            outline_node         = $("#outline");
        //++ variables that you're using as globals I've moved here so that they can be shared 
        //++ between the event handlers, but aren't poluting the global scope.  They are accessible
        //++ to each event handler via the closure property of this annonymous function.
        var id = null;
        //++ Since you're creating the code <INPUT> nodes, store a refernce to them at that time
        //++ instead of having to find them in the DOM again later.  Now, anzahlCodes doesn't need
        //++ to be available to both handlers.
        var code_input_nodes = [];
    
        /* when start button is clicked */
        $('.start_btn').click(function() {
            /* remove all previous input fields */
            dialog_fieldset_node.children().remove();
    
            /* save id */
            id = $(this).attr('name');
    
            /* get data from db */
            $.post("inc/get_start_order_infos.inc.php", { id: id },
                /* Callback */
                function(data){
                    /* make data globally available */
                    daten = data;
    
                    /* append infos_html to the fieldset */
                    //++ No need to build a string in a variable that you're only going to use once.  You might want 
                    //++ to build this out using DOM methods as I did below.  Since its only done once, there might 
                    //++ not be a difference performancy wise
                    dialog_fieldset_node.append(
                        '<label for="kunden_nr">Kunden-Nr.</label><input type="text" name="kunden_nr" id="kunden_nr" ' +
                        'value="' + data.kunden_nr + '" class="text ui-widget-content ui-corner-all" />' +
                        '<label for="sort">Sort</label><input type="text" name="sort" id="sort" value="' +
                        data.sort_nr + '" class="text ui-widget-content ui-corner-all" />'
                    );
    
    
                    //++ 1) `var e` to keep `e` from begin global.  If you want this side effect, you should be explicit about it.
                    //++ 2) Create nodes via DOM methods to avoid running the HTML parser. node variables are defined outside of the
                    //++ loop to avoid overhead of instantiation and scope-chain modification (minimal, but effective for large sets
                    //++ of iterations.
                    //++ 3) Append created nodes to a document fragment and then append the fragment to the `dialog_fieldset_node` to 
                    //++ avoid multiple, unnecessary DOM tree reflows (which are slow).
                    var fragment     = document.createDocumentFragment(),
                        label_node   = null, 
                        input_node   = null;
                        anzahlCodes  = 0;
                    //++ Seems this needs to be reset everytime
                    code_input_nodes = [];
    
                    /* For each element within the sub array codes */
                    for( var e in data.codes){
                        label_node = document.createElement("LABEL");
                        label_node.setAttribute("for", anzahlCodes);
                        label_node.innerHTML = "Code " + anzahlCodes;
    
                        input_node = document.createElement("INPUT");
                        input_node.setAttribute("type",  "text");
                        input_node.setAttribute("name",  "code" + anzahlCodes);
                        input_node.setAttribute("id",    "code" + anzahlCodes);
                        input_node.setAttribute("class", "text ui-widget-content ui-corner-all");
                        input_node.setAttribute("value", data.codes[e]);
    
                        //++ store a reference for later use
                        code_input_nodes.push(input_node);
    
                        /* append code_html to the fieldset */
                        fragment.appendChild(label_node);
                        fragment.appendChild(input_node);
                        anzahlCodes++;
                    }
                    dialog_fieldset_node.append(fragment);
    
                    dialog_node.dialog('open');
                    kunden_nr_node = $("#kunden_nr");
                    kunden_nr_node.select();
    
                }, 
                "json"
            );
    
            return false;
        });
    
        dialog_node.dialog({
            bgiframe: false,
            autoOpen: false,
            height:   350,
            modal:    true,
            buttons:  {
                'Auftrag starten': function() {
                    /* close dialog */
                    $(this).dialog('close');
    
                    /* create the info array to be submitted */
                    var arr_infos = [id, kunden_nr_node.attr('value'), $('#sort').attr('value')];
    
                    /* write inputs into the str_codes */
                    var str_codes = "";
                    for ( var i in code_input_nodes ) {
                        str_codes += (i ? "" : "<--->") + code_input_nodes[i].attr('value');
                    }
    
    
                    /* execute start */
                    $.post("inc/start_orders.inc.php", { 'corrected_infos[]':arr_infos, 'corrected_codes': str_codes },
                        /* Callback */
                        function(data){
                            $('#notice').remove();
    
                            /* if start was successful */
                            if (data.mstype == 'success'){
                                /* the tr where the button is inside */
                                //++ 1) Was this intentionally global?  Global variables are the slowest to access because they
                                //++ are at the end of the scope-chain (which *sometimes* makes a difference, depending on depth).
                                //++ 2) Instead of wrapping `tr` in `$()` every time you use it, just do it once.
                                var tr = $(
                                        $(".start_btn[name=" + id + "]").parent().parent()
                                    );
                                //++ Avoid calling `.children()` multiple times, just do it once.
                                var tr_children = tr.children();
    
                                /* remove red */
                                tr.removeClass('rot');
    
                                /* set text of Kunden-Nr. td */
                                tr_children.eq(3).text(arr_infos[1]);
    
                                /* set text of Sort td */
                                tr_children.eq(4).text(arr_infos[2]);
    
                                /* set text of Code td */
                                tr_children.eq(5).text(str_codes);
    
                                /* set text of start td */
                                tr_children.eq(8).text('ja');
    
                                /* prepend notice */
                                outline_node.prepend("<div id='notice'>" + data.ms + "</div>");
                            }
    
                            /* if not successful */
                            else {
                                outline_node.prepend("<div id='notice' class='notice_err'>" + data.ms + "</div>");
                            }
                        },
                        "json"
                    );
                },
    
                'Abbrechen': function() {
                    $(this).dialog('close');
                }
            }
        });
    });
    

    希望有所帮助。

    【讨论】:

    • 哇...令人惊讶的答案...现在要仔细阅读。谢谢!
    • 嘿贾斯汀,我收到错误:code_input_nodes[i].attr is not a function(第 107 行)想知道该行中的 if 语句是否也正确!?感谢所有 cmets 和可爱的帮助!
    • 所以你说了我所说的关于 TR 的内容,并将简单的字符串操作更改为 dom 元素操作......怎么更快?
    • @Darko 你错过了重点。 1)在点击处理程序中,我使用一个片段来构建与主文档分开的所有内容,然后将一个附加到 dom 中,以及 2)构建 str_codes 的循环根本不再搜索 DOM,只是访问存储在数组中的引用。
    • @max 抱歉,就像我说的,没有测试。尝试将 code_input_nodes.push(input_node); 更改为 code_input_nodes.push($(input_node)); 以将 jQuery 方法应用于节点,然后再将它们添加到数组中
    【解决方案2】:

    嘿,麦克斯。如果您还没有安装Firefox,我建议您安装Firebug。然后,您可以使用 Javascript Profiling 功能来找出代码中减速的确切位置。

    这是一个很好的 tutorial 使用 Firebug 进行分析。

    祝你好运。

    【讨论】:

    • 虽然这将有助于检测哪些功能很慢,但它无助于解释原因。
    • 贾斯汀,你需要知道它在哪里才能修复它,有时只是看代码,它在哪里并不明显......
    【解决方案3】:

    好吧,看看你的代码,你似乎犯的最大错误是在你的第二个回调(底部)中,你不断地重新选择行子元素。您应该真正将该常见查询结果分配给一个变量并使用它。我想你会看到性能的改进。这就是我要改变它的方式:

    /* beginning of callback code */
    
    if (data.mstype == 'success') {
    
        /* the pressed button */
        btn = $(".start_btn[name=" + id + "]");
        /* the tr where the button is inside */
        tr = $(btn).parent().parent();
        /* remove red */
        tr.removeClass('rot');
    
        /***** This is the change *****/
        var children = $(tr).children();
    
        /* set text of Kunden-Nr. td */
        children.eq(3).text(arr_infos[1]);
    
        /* set text of Sort td */
        children.eq(4).text(arr_infos[2]);
    
        /* set text of Code td */
        children.eq(5).text(str_codes);
    
        /* set text of start td */
        children.eq(8).text('ja');
        /* prepend notice */
        $("#outline").prepend("<div id='notice'>" + data.ms + "</div>");
    }
    
    /* end of callback code */
    

    除此之外,您还需要更具体地说明糟糕的性能究竟来自何处,为此您可以使用其他人指出的 Firebug。

    【讨论】:

    • 虽然重复使用$(tr) 会减慢速度,但最大的麻烦点是两个循环。第一个调用 HTML 解析器并在每次迭代时重构 DOM,第二个在每次迭代时搜索 DOM。
    • 除了 append() 之外,他没有操纵 dom,他操纵的字符串虽然丑陋且不是最优雅的方式,但比您建议的从 dom 创建节点要快
    • 将多个项目附加到 dom 节点并不比将它们附加到片段快。此外,他对 $("#someid") 进行了多次调用,这比在创建它们时将它们存储在数组中并稍后在数组中简单地访问它们要慢。
    • 他的问题是在旧版浏览器中,我无法测试其性能以确认或否认使用字符串或创建节点之间的区别。
    【解决方案4】:

    请注意,dialog_fieldset_node.children().remove(); 更适合通过 empty(); dialog_fieldset_node.empty();

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-08-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多