Binding around 5000 records using knockout

限于喜欢 提交于 2019-12-08 13:59:37

问题


I am trying to show around 5000 records in a web page using knockout observable array, which is taking hell lot of time,

Is there any way to handle this without pagination??

please help..

JS code in view model, data is coming via ajax call in gridData source:

 groupGrid.prototype.updateGrid = function (gridDataSource, groupGridOptions) {
        var self = this;
        self.ColumnName(groupGridOption.ColumnNameList); //   List of column name available in the data source.
        self.gridData(gridDataSource);    //  taking time while executing this code  
        self.totalRowCount(self.gridData().length);
        self.selectedItems.removeAll();
        self.selectedRowCount(0);
    };

HTML Code:

<tbody class="ngTBody" data-bind="foreach: gridData">
<tr class="ngdatarow">
<td>
    <span class="nameHeader" data-bind="text: $data[$root.ColumnName()[0]], click: $root.gridNameClick" style="cursor: pointer; text-decoration: underline"></span>
</td>
<td>
    <span class="displayBlock" data-bind="text: $data[$root.ColumnName()[1]]"></span>
</td>
<td>
    <span class="displayBlock" data-bind="text: $data[$root.ColumnName()[3]"></span>
</td>
</tr>
</tbody>

回答1:


I've done a lot of research into generating data tables quickly in the browser. The standard Knockout method of using the foreach and text bindings is quite slow. Simplifying the binding code can realize some improvement, as my Repeat binding shows. But the fastest way to generate a data table is to assemble it as a string in JavaScript code and then use innerHTML to insert it into the DOM. My Table binding is an example of how this can be done.

In your case, a custom binding that assembles the data table will provide a huge speed increase. Here's a custom binding that I put together based on your example:

ko.bindingHandlers.myDataTable = {
    init: function () {
        return { controlsDescendantBindings: true };
    },
    update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
        var output = [],
            value = valueAccessor(),
            columns = ko.unwrap(value.columns),
            column1 = columns[0],
            column2 = columns[1],
            column3 = columns[2],
            data = ko.unwrap(value.data),
            dataLength = data.length,
            clickFunction = value.click;
        output.push('<table><tbody class="ngTBody">');
        for (var i = 0; i < dataLength; ++i) {
            output.push('<tr class="ngdatarow"><td><span class="nameHeader" data-index="');
            output.push(i + '">');
            output.push(data[i][column1]);
            output.push('</span></td><td><span class="displayBlock">');
            output.push(data[i][column2]);
            output.push('</span></td><td><span class="displayBlock">');
            output.push(data[i][column3]);
            output.push('</span></td></tr>');
        }
        output.push('</tbody></table>');
        element.innerHTML = output.join('');
        $(element).on('click', 'span.nameHeader', function (event) {
            var index = event.target.getAttribute('data-index');
            if (index) {
                clickFunction(data[index], event);
            }
        });
    }
};

For comparison, I put together the following two fiddles:

  1. http://jsfiddle.net/mbest/csP6k/ using foreach. It takes between 2 and 3 seconds to render the table on my computer.
  2. http://jsfiddle.net/mbest/8cKuP/ using the custom binding. It's almost 25 times faster to render the table.

I also created an example that includes click bindings and uses ko.applyBindingsToDescendants, but it takes much longer than the example above because it has to set up 5000 event handlers versus just one.




回答2:


I've been wrestling a bit with performance in my KO app recently. I just added an answer to another question, with some thoughts, here:

Knockout subscribe is blocking the page

Arising from that, here is a fiddle that shows you don't need much to get a delay - this demo has 3000 items and generates a simple div for each item. As per the blog post linked to above, the items are built first in a JS array, before being turned into an observableArray. So I am pretty sure by this stage the performance problem you see is quite simply the DOM manipulation arising from KO's work.

http://jsfiddle.net/HBYyL/1/

This isn't really a fix for the performance problem, but shows that a delay is probably inevitable if you loop over thousands of items and it uses a pattern where you can ensure you have a loading spinner appear before the long KO operation, then hide it afterwards. So it improves the UX, at least.

Ensure you can load a spinner:

// Show the spinner immediately...
$("#spinner").show();

// ... by using a timeout.
window.setTimeout(function() {
    ko.applyBindings(vm)  
}, 1)

Hide the spinner:

<div data-bind="template: {afterRender: hide}">

Update 1:

I remembered an old technique back from when I was working on a set top box with Opera, building UI using DOM manipulation. It was appalling slow, so the solution was to store large chunks of HTML as strings, and load the strings by setting the innerHTML property. An early version of a single-page app. Anyway, as part of my ongoing fiddling in this area, here is a jsfiddle that shows 5000 items being loaded via KO, but going via an intermediate html computed.

http://jsfiddle.net/9ZF3g/1/

In other words, you compute the HTML from your list, and set it in one go, using the html binding. I've set this example to 5000 items, and it is almost instant.

The massive downside is that it severely limits what you can do with binding inside each item...


Update 2:

Here is a fiddle showing some semblance of linking back from the items to the KO view model via the array index. You could probably use constructs like this to do most everything KO supports, but you'd lose some of the magic; you'd have to write more code yourself, I think.

http://jsfiddle.net/9ZF3g/2/


Update 3:

This fiddle shows a technique for using a timeout to push each item into the list one-by-one. By putting the timeout between the push operations, the DOM updates item-by-item. So the overall render time is still quite large, but the user gets immediate feedback:

http://jsfiddle.net/rosenfeld/7TwcV/1/


Update 4:

Showing the above technique, but doing something useful by deleting a record.

http://jsfiddle.net/9ZF3g/3/



来源:https://stackoverflow.com/questions/24912271/binding-around-5000-records-using-knockout

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!