4
votes

I'm currently working on a project that's using .NET MVC on the back end and Backbone (v1.1) with Marionette JS (v1.8.5) on the front end and I am experiencing some performance issues when trying to render a Composite view with 200 results or item views.

I first came across this SO post of a similar issue which helped me get a newer version of marionette (v1.8.5).

Handlebars templates are being used to show a html table with each itemView being wrapped in a tbody (this has a more complex accordion view within it hence tbody being used rather than a more semantic tr)

The Composite view is being initialised and shown in a Marionette region when it's sub app is initialised.

Here's a simplified version of my code.

Activities sub app

'use strict';

App.module('Activities', function(){
    this.startWithParent = false;
});

App.addRegions({
    RegionActivities: '#region-Activities' // <= this lives in .NET View of project
});

App.Activities.addInitializer(function (options) {

    var module = this,
        activities = new Collections.Activities();

    module.activitiesView = new Views.ActivitiesView({
        collection: activities
    });

    App.RegionActivities.show(module.activitiesView);

    activities.fetch();

});

Activities Composite View

'use strict';

Views.ActivitiesView = Backbone.Marionette.CompositeView.extend({

    template: Handlebars.templates['activities/activities'],

    itemView: Views.ActivityView,

    itemViewContainer: '.admin-table--activities', // <= html table that items are appended to

});

Activities Item View

'use strict';

Views.ActivityView = Backbone.Marionette.ItemView.extend({

    template: Handlebars.templates['activities/activity'],

    tagName: 'tbody'

});

I read the Marionette Docs on the better performing doc fragment

So the performance issue that I'm having is that all 200 items are being appended one at a time and not utilising docFragment buffer. Through debugging I believe this is because the view is being instantiated with an empty Activities collection before the fetch, so Marionette thinks I'm updating the collection and sets the isBuffering to false.

I've also tried instatiating the activitiesView inside the fetch success callback like so;

activities.fetch({
    success: function(newCollection) {
        module.activitiesView = new Views.ActivitiesView({
            collection: newCollection
        });

        App.RegionActivities.show(module.activitiesView);
    }
});

This does append the 200 items to the collections elBuffer (takes about 4 seconds) but then takes another 50 seconds or so before anything is shown on screen. Debugging suggests the onShow method that's triggered by the collection could be causing this delay as it loop through each item one by one.

Have I missed something?

Am I doing everything in the right places?

Is what I'm trying to achieve even possible?

Seems strange to me that this could be so difficult.

Thanks for you time. Tom

Edit

Here are both the composite view and item view templates.

Activities CompositeView template

<table class="admin-table admin-table--accordion admin-table--activities">

    <colgroup>
        <col width="10%">
        <col width="22%">
        <col width="23%">
        <col width="23%">
        <col width="22%">
    </colgroup>

    <thead>
        <tr>
            <th>Type</th>
            <th>Vessel</th>
            <th>Activity</th>
            <th>Information</th>
            <th>Review Status</th>
        </tr>
    </thead>

</table>

Activity ItemView template (each wrapped in tbody)

<tr class="table-title">
    <td>Col 1 data</td>
    <td>Col 2 data</td>
    <td>Col 3 data</td>
    <td>Col 4 data</td>
    <td Col 5 data</td>
</tr>

<tr class="table-content">
    <td colspan="5">More info in here...</td>
</tr>

Update

Results from chrome dev tools javascript CPU profile:

Top 3 items all refer to Marionette triggerMethod taking up 80% of CPU.

Dev tools CPU Profile Drilldown

Full sized image at: http://i.stack.imgur.com/JjT3Z.png

1
Could you post the templates? This shouldn't make a difference, but try activities.fetch().done(function() { App.RegionActivities.show(module.activitiesView); })Meistro
Thanks @Meistro but that didn't help unfortunately. I'm now using the 2nd version from my question where I perform the fetch and then on success instantiate the view. Console.logging the activiesView instance happens in a 2nd. Problem seems to be around rendering the collection on screen. Mainly via region.show()Tom Gillard
Can you post your onShow function? I don't see why calling that function a few hundred times should be causing you problems (50 sec delay). Your templates seem simple enough too. I have an app that very similarly inserts lists that are 200 items long and don't see the kind of perf issues you are running into. Something is missing from this equation.Andrew Hubbs
Also, when you drill down into the triggerMethod portion of the profile what function are you getting to that is your own code?Andrew Hubbs
@AndrewHubbs I'm not using an onShow function in my composite or item views. I'll back to you regarding the devtools profile. I didn't notice anything relating to my own code from memoryTom Gillard

1 Answers

3
votes

The buffer will be used when the collection is reset, so you could try

activities.fetch({reset: true});

The other option is to wait until the fetch is done (using deferreds/promises) before initializing and displaying your view.