动态创建的 Html 表对于大量记录渲染速度很慢

Sou*_*jee 3 html javascript jquery html-table dynamic

这就是我在通过 ajax 调用从数据库获取数据后创建表并渲染到 aspx 页面的方式。我检查了存储过程执行时间,只有 1.5 秒,页面中的表大约在 7-8 秒内呈现。我需要优化速度。有什么建议我该怎么做吗?

$.ajax({
    method: 'POST',
    //async: false,
    data: json_data,
    dataType: 'json',
    url: json_url
}).success(function (response) {
    if (response.IsError) {
        showError('Did not receive values from XRM.');
    } else {
        var webResponse = response;
        var dataSet = webResponse["Orders"];
        var statuses = [];

        //console.log(dataSet);

        if (table_role == 'pending') {
            statuses = [
                {
                    'id': 39,
                    'name': 'Pending Scheduling',
                    'class': 'schedule',
                    'color': '#7C44BC',
                    'order': 0
                },
                {
                    'id': 40,
                    'name': 'Pending Screening',
                    'class': 'screening',
                    'color': '#8e8e00',
                    'order': 1
                },
                {
                    'id': 41,
                    'name': 'Pending Pre-Auth',
                    'class': 'preauth',
                    'color': '#cc8500',
                    'order': 2
                },
                {
                    'id': 42,
                    'name': 'Ready For Confirmation',
                    'class': 'confirm',
                    'color': '#cc0070',
                    'order': 3
                },
                {
                    'id': 1,
                    'name': 'Open',
                    'class': 'open',
                    'color': null,
                    'order': 4
                },
                {
                    'id': 2,
                    'name': 'New Order',
                    'class': 'neworder',
                    'color': null,
                    'order': 5
                },
                {
                    'id': 3,
                    'name': 'Changed',
                    'class': 'changed',
                    'color': '#cd4e3e',
                    'order': 6
                },
                {
                    'id': 5,
                    'name': 'Approved',
                    'class': 'approved',
                    'color': '#6aa745',
                    'order': 7
                },
                {
                    'id': 6,
                    'name': 'On Hold',
                    'class': 'onhold',
                    'color': '#009acd',
                    'order': 8
                },
                {
                    'id': 11,
                    'name': 'In Queue',
                    'class': 'queue',
                    'color': null,
                    'order': 9
                }
            ];

            // For customers, change labels
            if (is_customer) {
                statuses[5]['name'] = 'Submitted to SMS';
                statuses[9]['name'] = 'Approved';
            }
        } else if (table_role == 'preauth') {
            statuses = [
                {
                    'id': 29,
                    'name': 'More Info Needed',
                    'class': 'moreinfo',
                    'order': 0
                },
                {
                    'id': 43,
                    'name': 'Phone Order',
                    'class': 'phone',
                    'order': 1
                },
                {
                    'id': 30,
                    'name': 'Received Order',
                    'class': 'received',
                    'order': 2
                },
                {
                    'id': 31,
                    'name': 'Waiting on Documentation',
                    'class': 'waiting',
                    'order': 3
                },
                {
                    'id': 32,
                    'name': 'Courtesy Call Pending',
                    'class': 'courtesy-pending',
                    'order': 4
                },
                {
                    'id': 33,
                    'name': 'Courtesy Call Complete',
                    'class': 'courtesy-complete',
                    'order': 5
                },
                {
                    'id': 34,
                    'name': 'In Pre-Authorization',
                    'class': 'preauth',
                    'order': 6
                },
                {
                    'id': 35,
                    'name': 'Additional Docs Needed',
                    'class': 'addtldocs',
                    'order': 7
                },
                {
                    'id': 36,
                    'name': 'Pending Authorization',
                    'class': 'pending',
                    'order': 8
                }
            ];
        }

        // Remove any "sorting" backgrounds from headers
        $th.removeClass('otable__sort');

        // If no orders available
        if ($.isEmptyObject(dataSet)) {
            $table.find('.otable__empty').css('display', 'table-cell');
        } else {
            // Loop through orders
            $.each(dataSet, function (key, value) {
                if (table_role == 'pending') {
                    var id = (value['ID'] == null) ? '' : value['ID'],
                        date = (value['ID'] == null) ? '' : value['ExamDate'],
                        billing = (value['BillCode'] == null) ? '' : value['BillCode'],
                        scanner = (value['ScannerCode'] == null) ? '' : value['ScannerCode'],
                        units = (value['Unit'] == null) ? '' : value['Unit'],
                        by = (value['EmployeeName'] == null) ? '' : value['EmployeeName'],
                        status = value['StatusID'],
                        customer = value['Customer'],
                        location = '';

                    // Build location string
                    if (value['Facility']) {
                        location += value['Facility'] + '<br>';
                    }
                    if (value['City'] && value['State']) {
                        location += value['City'] + ', ' + value['State'];
                    }
                } else if (table_role == 'preauth') {
                    var id = (value['ID'] == null) ? '' : value['ID'],
                        poid = (value['POID'] == null) ? '' : value['POID'],
                        site = (value['Site'] == null) ? '' : value['Site'],
                        patient = (value['PatientName'] == null) ? '' : value['PatientName'],
                        insurance_co = (value['InsuranceCo'] == null) ? '' : value['InsuranceCo'],
                        received_date = (value['ReceivedDate'] == null) ? '' : value['ReceivedDate'],
                        exam_date = (value['ExamDate'] == null) ? '' : value['ExamDate'],
                        updated_date = (value['UpdatedDate'] == null) ? '' : value['UpdatedDate'],
                        status = value['StatusID'],
                        priority = (value['Priority'] == null) ? 0 : value['Priority'],
                        custid = (value['custID'] == null) ? '' : value['custID'];
                }

                // Find status from ID
                var object = $.grep(statuses, function (element, index) {
                    return element['id'] == status;
                });
                object = object[0];

                // Get status name
                var status_name = object['name'],
                    status_pos = object['order'],
                    status_class = object['class'],
                    datestring = '',
                    received_datestring = '',
                    exam_datestring = '',
                    updated_datestring = '';

                // Build date strings
                if (date) {
                    datestring = splitDate(date);
                }
                if (received_date) {
                    received_datestring = splitDate(received_date);
                }
                if (exam_date) {
                    exam_datestring = splitDate(exam_date);
                }
                if (updated_date) {
                    updated_datestring = splitDate(updated_date);
                }

                // Build Time strings
                if (updated_date) {
                    updated_timestring = splitTime(updated_date);
                }
                else {
                    updated_timestring = '';
                }

                // Convert priority for sorting
                var priority_order;
                if (priority) {
                    if (priority == 0) {
                        priority_order = 2;
                    } else if (priority == 5) {
                        priority_order = 1;
                    } else if (priority == 10) {
                        priority_order = 0;
                    }
                }

                // HTML for table row
                var $row;

                if (table_role == 'pending') {
                    if (!is_customer && (is_scheduler || is_retail)) {
                        $row = $('<tr class="otable__row otable__row--' + status_class + '">' +
                                  '<td class="otable__id"><a href=/Customer/FDGOrders.aspx?oid=' + id + '>' + id + '</a></td>' +
                                  '<td class="otable__date" data-sort-value="' + date + '"><a href=/Customer/FDGOrders.aspx?oid=' + id + '>' + datestring + '</a></td>' +
                                  '<td class="otable__billing"><a href=#rptPendingGroupByOrder onclick=showDialog(\'/CustomerProfile.aspx?cid=' + customer + '\')>' + billing + '</a></td>' +
                                  '<td class="otable__scanner"><a href=/Customer/FDGOrders.aspx?oid=' + id + '>' + scanner + '</a></td>' +
                                  '<td class="otable__loc"><a href=#rptPendingGroupByOrder onclick=showDialog(\'/CustomerProfile.aspx?cid=' + customer + '\')>' + location + '</a></td>' +
                                  '<td class="otable__units"><a href=/Customer/FDGOrders.aspx?oid=' + id + '>' + units + '</a></td>' +
                                  '<td class="otable__by"><a href=/Customer/FDGOrders.aspx?oid=' + id + '>' + by + '</a></td>' +
                                  '<td class="otable__status" data-status-id="' + status + '" data-sort-value="' + status_pos + ' ' + date + '"><a href=/Customer/FDGOrders.aspx?oid=' + id + '>' + status_name + '</a></td>' +
                                '</tr>');
                    } else if (!is_customer && (!is_scheduler || !is_retail)) {
                        $row = $('<tr class="otable__row otable__row--">' +
                                  '<td class="otable__id"><a href=/Customer/FDGOrders.aspx?oid=' + id + '>' + id + '</a></td>' +
                                  '<td class="otable__date" data-sort-value="' + date + '"><a href=/Customer/FDGOrders.aspx?oid=' + id + '>' + datestring + '</a></td>' +
                                  '<td class="otable__billing"><a href=#rptPendingGroupByOrder onclick=showDialog(\'/CustomerProfile.aspx?cid=' + customer + '\')>' + billing + '</a></td>' +
                                  '<td class="otable__scanner"><a href=/Customer/FDGOrders.aspx?oid=' + id + '>' + scanner + '</a></td>' +
                                  '<td class="otable__loc"><a href=#rptPendingGroupByOrder onclick=showDialog(\'/CustomerProfile.aspx?cid=' + customer + '\')>' + location + '</a></td>' +
                                  '<td class="otable__units"><a href=/Customer/FDGOrders.aspx?oid=' + id + '>' + units + '</a></td>' +
                                  '<td class="otable__by"><a href=/Customer/FDGOrders.aspx?oid=' + id + '>' + by + '</a></td>' +
                                  '<td class="otable__status" data-status-id="' + status + '" data-sort-value="' + status_pos + ' ' + date + '"><a href=/Customer/FDGOrders.aspx?oid=' + id + '>' + status_name + '</a></td>' +
                                '</tr>');
                    } else {
                        $row = $('<tr class="otable__row otable__row--customer otable__row--' + status_class + '">' +
                                  '<td class="otable__id"><a href=/Customer/FDGOrders.aspx?oid=' + id + '>' + id + '</a></td>' +
                                  '<td class="otable__date" data-sort-value="' + date + '"><a href=/Customer/FDGOrders.aspx?oid=' + id + '>' + datestring + '</a></td>' +
                                  '<td class="otable__units"><a href=/Customer/FDGOrders.aspx?oid=' + id + '>' + units + '</a></td>' +
                                  '<td class="otable__by"><a href=/Customer/FDGOrders.aspx?oid=' + id + '>' + by + '</a></td>' +
                                  '<td class="otable__status" data-status-id="' + status + '" data-sort-value="' + status_pos + ' ' + date + '"><a href=/Customer/FDGOrders.aspx?oid=' + id + '>' + status_name + '</a></td>' +
                                '</tr>');
                    }
                } else if (table_role == 'preauth') {
                    var insurance_sort_val = priority_order;
                    if (exam_date && exam_date.indexOf('0001-01-01') < 0) {
                        insurance_sort_val = priority_order + ' ' + exam_date;
                    } else if (received_date) {
                        // Force items with null exam date to bottom, then sort by received date
                        insurance_sort_val = priority_order + ' Z ' + received_date;
                    }

                    $row = $('<tr class="otable__row otable__row--' + status_class + '">' +
                                  '<td class="otable__id"><a href="">' + id + '</a></td>' +
                                  '<td class="otable__site"><a href="/Customer/FDGOrders.aspx?oid=' + id + '">' + site + '</a></td>' +
                                  '<td class="otable__by"><a href="">' + patient + '</a></td>' +
                                  '<td class="otable__insurance otable__priority--' + priority + '" data-sort-value="' + insurance_sort_val + '"><a href="">' + insurance_co + '</a></td>' +
                                  '<td class="otable__date" data-sort-value="' + received_date + '"><a href="">' + received_datestring + '</a></td>' +
                                  '<td class="otable__date" data-sort-value="' + exam_date + '"><a href="">' + exam_datestring + '</a></td>' +
                                  '<td class="otable__status" data-status-id="' + status + '" data-sort-value="' + status_pos + ' ' + exam_date + '"><a href="">' + status_name + '</a></td>' +
                                  '<td class="otable__date--wide" data-sort-value="' + updated_date + '"><a href="">' + updated_datestring + ' ' + updated_timestring + '</a></td>' +
                                '</tr>');

                    $row.find('a').on('click', function (e) {
                        if (!$(this).parent('.otable__site').length) {
                            e.preventDefault();

                            showDialog('/Customer/FDGPatientOrder.aspx?oid=' + id + '&poid=' + poid);
                        }
                        if ($(this).parent('.otable__site').length) {
                            e.preventDefault();

                            showDialog('/CustomerProfile.aspx?cid=' + custid);
                        }
                    });
                }

                // Append to table body
                $table.find('tbody').append($row);
            });
        }

        // Fire event
        //$table.trigger('all-rows-added');
        //$table.off('all-rows-added');

        // Table sorting functionality
        if (table_role == 'pending') {
            initTableSorting($table, 7);
        } else if (table_role == 'preauth') {
            initTableSorting($table, 3);
        }

        // Status filter functionality
        var $list = $table.find('.otable__status__list');
        // Clear previous filters
        $list.find('ul>li:not(:first)').remove();

        // Loop through statuses to see which are contained in the table
        $.each(statuses, function (key, value) {
            if ($table.find('tbody').has('.otable__status[data-status-id="' + value.id + '"]').length > 0) {
                var $li;

                if (table_role == 'pending') {
                    $li = $('<li>' +
                              '<button data-filter="' + value.id + '" data-color="' + value.color + '">' +
                                '<div class="otable__legend"></div>' +
                                value.name +
                              '</button>' +
                            '</li>');
                } else {
                    $li = $('<li>' +
                              '<button data-filter="' + value.id + '">' +
                                '<div class="otable__legend"></div>' +
                                value.name +
                              '</button>' +
                            '</li>');
                }

                // Add the list item
                $li = $li.appendTo($list.find('ul'));

                // Set the legend color
                var $btn = $li.find('button'),
                    filter = $btn.data('filter'),
                    color = $btn.data('color');

                if (color !== null) {
                    $li.find('.otable__legend').css('background', color);
                }
            }
        });

        $th.on('click', function (e) {
            // Check if main filter is being clicked, otherwise don't sort
            if ($list.has(e.target).length > 0) {
                var $target = $(e.target).closest('button'),
                    filter = $target.data('filter');

                // Sort all rows by ascending status
                $table.find('th').eq(7).stupidsort('asc');

                // Style actively selected item
                $list.find('li').removeClass('otable__status__checked');
                $target.parent('li').addClass('otable__status__checked');

                if (filter == 'all') {
                    $table.find('tbody > tr').css('display', 'table-row');
                } else {
                    $table.find('tbody > tr').css('display', 'none');
                    $table.find('tbody > tr').has('.otable__status[data-status-id="' + filter + '"]').css('display', 'table-row');
                }

                // Force hide list when option clicked
                $list.css({
                    'clip': 'rect(0 0 0 0)',
                    'opacity': 0
                });
                // Restore default behavior once mouse is moved
                $table.mousemove(function () {
                    $list.removeAttr('style');
                });

                // Prevent main sorting action
                return false;
            }
        });

        // Sticky table header
        $table.stickyTableHeaders();

        // Remove loading indicator
        $table.find('.otable__loading').css('display', 'none');
    }
}).fail(function (jqxhr,

Amm*_*san 7

我们遇到了类似的场景,我们有超过 30000 条记录要在同一页面上呈现,API 表现良好,但 UI 在性能方面变得可怜。Chrome 和 Firefox 以某种方式幸存下来,并以 10 到 15 秒的延迟呈现记录,但 IE 是一场噩梦,每次我们访问该页面时,浏览器都会冻结。这个问题的解决方案是渐进但优雅的,我们必须转向基础知识。我将在这里分解我的解决方案的一个子集

  1. $.eachand_.each循环替换为本机for(var i = 0; i < lenght; i += 1)循环。这样做大大提高了性能。为了证明这个概念,当时我创建了一个 jsperf(查看此处)性能基准测试,它显示出巨大的性能提升。例如,在您的情况下,将$.each(dataSet, function (key, value) {and替换$.each(statuses, function (key, value) {为本机 for 循环。请参阅此图片比较循环 在此输入图像描述

  2. 减少 JQuery 选择器的数量也被证明是有益的,而不是创建像 之类的内存 jquery 元素$('<div>bla bla bla</div>');,而是使用字符串连接,最后使用 example$el.html()被证明是非常有效的。在你的例子中,你不需要做类似的事情$row = $('<tr class ...,然后绑定函数$row.find('a').on('click',你只需要创建一个长字符串并附加其中的所有内容,最后将该长字符串附加到 DOM。如果您只是这样做以将单击事件绑定到每一行(OMG),则可以通过使用 JQuery 在单击父级并将子级作为过滤器时实现类似的行为,例如$('table').on('click', 'tr', myClickFunc);。这将仅绑定单个单击事件而不是数千个单击事件,从而带来巨大的性能提升。

  3. 通过避免 JQuery 过滤方法(如$.grepor$.map$.filter等)并使用本机循环,也带来了巨大的改进。
  4. 通过使用jQuery 元素缓存并避免对 DOM 进行频繁查询,在不同页面中取得了显着的成果。我认为你的缓存做得很好,但尽量避免频繁的 DOM 查询。
  5. 避免循环中的多个方法调用也会消耗整体加载时间,因此将多个方法调用组合到单个方法调用中,并在提高性能的情况下执行常见的操作。就像您可以splitDate获取一个数组并返回一个数组,而不是多个方法调用。
  6. 最后是宝石,也是我的最爱,我使用自定义数据网格来填充表格数据,但自从我遇到W2UI Grid以来,我就爱上了它。它可以一次渲染数百万条记录,它的工作原理是仅渲染少数记录以显示,当滚动发生时,它会渲染其他记录并从 DOM 中删除不再可见的记录。配置非常简单,因此您也可以使用此网格代替自定义表格(如果您不使用分页)。

因此,这只是您可以采取的改善加载时间和渲染性能的直接措施的简要总结。

一个更重要的提示是,您还应该检查来自服务器的响应的大小,如果它很大并且以兆字节为单位,那么您还必须优化您的 API,并且只向 UI 提供它需要的东西,不要提供过多的东西,因为它会减慢用户界面的速度。