-1

I'm building a data grid with thousands of list items and no pagination. As a result, the performance with live-binding is, understandably, very poor. With ~20 list items, there is no lag whatsoever.

<thead>
    {{#each columns}}
        <tr><td can-click="sort">{{name}}</td></tr>
    {{/each}}
</thead>
<tbody>
    {{#each thousandsOfItems}}
        <tr><td>{{name}}</td></tr>
    {{/each}}
</tbody>

Is there a way for part of my template to be live bound, such as the <thead>, but use simple string concatenation/injection for the <tbody>?

Edit

My approach may be the problem. Thousands of static <tr>s are likely quite laggy too. It's been suggested to me that I try adding/removing rows as the page is scrolled.

ramblinjan
  • 6,578
  • 3
  • 30
  • 38
Steven Vachon
  • 3,814
  • 1
  • 30
  • 30
  • Are you using can.Stache or can.Mustache? can.Stache has faster performance but perhaps not fast enough to be considered a solution. – air_hadoken Oct 04 '14 at 01:47

1 Answers1

0

I made a JSPerf rendering 4000 rows in a single column table, which you can check out here:

http://jsperf.com/can-stache-table-render

Some takeaways from these experiments:

  • without live-binding you can get a 10x performance boost. If you need more than that, though, you'll probably have to do perceived performance boosts.

  • When not live binding any data, there's no performance boost in using {{#each}} versus just {{#thousandsOfRows}}

  • Using divs instead of table rows was something I was interested in trying since you could conceivably see incremental drawing faster (but really, with all of this running synchronously, the UI will lock until it's all drawn this way), but it's 35% slower to draw the whole table, and probably is best avoided.

So based on this, what can we do?

  • Page the table, or rather, draw many tables of a fixed size until you run out of data. The first screen will render quickly and the rest will fill in as you go. Make use of setTimeout() to draw the next table, so as to not lock the UI.

  • Use live-binding, but page it in using a pattern like this:

    var i=0; function loop() { while(i < source_list.length) { list_in_live_binding_context.push.apply( list_in_live_binding_context, source_list.splice(i, 20) ); i += 20; setTimeout(loop, 4); } }

  • Do one of the two previous things, but hide the results behind a screen with a spinner until the rendering is finished

  • Use jquery-ui-scrollable (https://github.com/bseth99/jquery-ui-scrollable) to detect when elements have been scrolled into view -- use a Stache helper to only render items once they're scrolled in to view, but you'll need to reserve the space first. This can be helpful if each individual row has complex rendering associated with it. You can delay the details until they're necessary.

Sorry it's not a simple answer, but it's not a simple problem. :) HTH, anyway.

air_hadoken
  • 611
  • 4
  • 5