Search code examples
javascriptjqueryhtmltemplatingrivets.js

Slow performance rendering in client templating engine (Rivets.js)


I'm rendering an array of of about 1000 objects. The html bindings are very heavy (see below). It's taking about 5 seconds to rivets.bind().

Any suggestions on improving performance? I don't think I can afford to bind in chunks as I'm using a pagination/sorting library in conjuction that needs the entire array in order to sort/paginate.

Here is my HTML for each object (tracks):

<div rv-each-track="tracks" class="track-row row has-hover" rv-download-url="track.direct_path.download_path" rv-api-key="track.track.apikey" rv-media-url="track.direct_path.audio" rv-track-title="track.track.name" rv-wave-data="track.direct_path.wave_default" rv-wave-progress-data="track.direct_path.wave_progress">
<div class="mobile-margin">
    <div class="track-hover desktop-only">
        <div class="hover-play icon-play inline-play"></div>
        <div class="hover-title"><a href="#" class="track-link">{track.track.name}</a><span rv-class="track.track.staff_pick | staffPickClass" data-toggle="tooltip" data-original-title="Staff Pick"></span></div>
        <div class="hover-links">
            <div class="item hamburger holds-tooltip main-hover-item icon-hamburger" data-toggle="tooltip" data-original-title="Alternate Versions"></div>
            <div class="item share main-hover-item popover-button icon-share" data-target="#not-ready-popover"><div class="tooltip-holder holds-tooltip" data-toggle="tooltip" data-original-title="Share Track"></div></div>
            <div class="item playlist icon-playlist-add popover-button holds-tooltip main-hover-item" data-target="#playlist-popover" data-toggle="tooltip" data-original-title="Add to Playlist"></div>
            <div class="item download icon-download holds-tooltip main-hover-item" data-toggle="tooltip" data-original-title="Download Track"></div>
            <div class="item cart last icon-cart-plus holds-tooltip main-hover-item popover-button" data-target="#not-ready-popover" data-toggle="tooltip" data-original-title="Add to Cart"></div>
            <div class="item remove last icon-x holds-tooltip main-hover-item" data-toggle="tooltip" data-original-title="Remove Track" rv-data-delete-track-id="track.track.apikey"></div>
            <div class="clearfix"></div>
        </div>
        <div class="clearfix"></div>
        <div class="track-variations">
            <div rv-each-variation="track.variations.tracks" class="row variation-row" rv-data-track-id="track.track.apikey" rv-api-key="variation.track.apikey" rv-media-url="variation.direct_path.audio" rv-track-title="variation.track.name" rv-wave-data="variation.direct_path.wave_default" rv-wave-progress-data="variation.direct_path.wave_progress">
                <div class="col-md-8 variation-title">{variation.track.name}</div>
                <div class="col-md-2 variation-length">{variation.track.tracklength}</div>
                <div class="track-hover variation">
                    <div class="hover-play icon-play inline-play"></div>
                    <div class="hover-title"><a class="track-link">{variation.track.name}</a></div>
                    <div class="hover-links">
                        <div class="item share popover-button icon-share" data-target="#not-ready-popover"><div class="tooltip-holder holds-tooltip" data-toggle="tooltip" data-original-title="Share Track"></div></div>
                        <div class="item playlist popover-button icon-playlist-add" data-target="#playlist-popover" data-toggle="tooltip" data-original-title="Add to Playlist" data-placement="left"></div>
                        <div class="item download icon-download" rv-data-media-url="track.direct_path.download_path" data-toggle="tooltip" data-original-title="Download Track" data-placement="left"></div>
                        <div class="item cart last icon-cart-plus holds-tooltip popover-button" data-target="#not-ready-popover" data-toggle="tooltip" data-original-title="Add to Cart"></div>
                        <div class="item last remove main-hover-item icon-x"></div>
                        <div class="clearfix"></div>
                    </div>
                    <div class="clearfix"></div>
                </div>
            </div>
            <div class="no-variations" rv-hide="track.variations.tracks | shouldHideNoVariations">There are no alternate versions of this track.</div>
        </div>
    </div>
    <div class="col-md-4 first-title desktop-only"><a class="offset-left track-title-link track-link" href="">{track.track.name}</a><span rv-class="track.track.staff_pick | staffPickClass" data-toggle="tooltip" data-original-title="Staff Pick"></span></span></div>
    <div class="col-md-3 genre desktop-only"><span class="offset-left">{track.genre}</span></div>
    <div class="col-md-2 mood desktop-only"><span class="offset-left">{track.mood}</span></div>
    <div class="col-md-2 canvas desktop-only"><div class="mini-wave offset-left" rv-style-background-image="track.direct_path.wave_canvas"></div></div>
    <div class="col-md-1 last-title duration desktop-only"><span>{track.track.tracklength}</span></div>
    <div class="col-md-1 last-title last-played pull-right desktop-only">{track.lastPlayed}</div>

    <div class="mobile-play icon-play pull-left mobile-only inline-play"></div>
    <div class="mobile-track-title mobile-only track-row-item-margin"><div class="track-title-link">{track.track.name}</div><span rv-class="track.track.staff_pick | staffPickClass"></span></div>
    <div class="mobile-track-buttons pull-right">
        <div class="pull-right mobile-only mobile-button mobile-track-menu-button left-margin icon-plus"></div>
        <div class="pull-right mobile-only mobile-button mobile-variations-button icon-hamburger"></div>
    </div>

    <div class="is-staff-pick hidden">{track.track.staff_pick | staffPickValue}</div>
    <div class="instrument hidden">{track.instrument}</div>
    <div class="industry hidden">{track.industry}</div>
    <div class="tempo hidden">{track.tempo}</div>
    <div class="aggregated-terms hidden">{track.tag_list} {track.track.name}</div>
    <div class="date-last-played hidden">{track.dateLastPlayed}</div>
    <div class="clearfix"></div>
</div>
<div class="track-variations mobile-only">
    <div rv-each-variation="track.variations.tracks" class="row variation-row" rv-api-key="track.track.apikey" rv-media-url="variation.direct_path.audio" rv-track-title="variation.track.name">
        <div class="mobile-margin">
            <div class="col-md-8 variation-title desktop-only">{variation.track.name}</div>
            <div class="col-md-2 variation-length desktop-only">{variation.track.tracklength}</div>
            <div class="track-hover variation">
                <div class="hover-play icon-play inline-play"></div>
                <div class="hover-title"><a href="" class="track-link">{variation.track.name}</a></div>
                <div class="hover-links">
                    <div class="item share popover-button icon-share" data-target="#not-ready-popover"><div class="tooltip-holder holds-tooltip" data-toggle="tooltip" data-original-title="Share Track"></div></div>
                    <div class="item playlist popover-button icon-playlist-add" data-target="#playlist-popover" data-toggle="tooltip" data-original-title="Add to Playlist" data-placement="left"></div>
                    <div class="item download icon-download" data-toggle="tooltip" data-original-title="Download Track" data-placement="left"></div>
                    <div class="item cart last icon-cart-plus holds-tooltip popover-button" data-target="#not-ready-popover" data-toggle="tooltip" data-original-title="Add to Cart"></div>
                    <div class="item last remove main-hover-item icon-x"></div>
                    <div class="clearfix"></div>
                </div>
                <div class="clearfix"></div>
            </div>

            <div class="mobile-play icon-play pull-left mobile-only inline-play"></div>
            <div class="mobile-track-title mobile-only track-row-item-margin"><div class="track-title-link">{variation.track.name}</div></div>
            <div class="mobile-track-buttons pull-right">
                <div class="pull-right mobile-only mobile-button mobile-track-menu-button left-margin icon-plus"></div>
            </div>

        </div>
    </div>
    <div class="no-variations" rv-hide="track.variations.tracks | shouldHideNoVariations">There are no alternate versions of this track.</div>
</div>


Solution

  • I've come to the the conclusion that yes, there are minor performance improvements that I could potentially implement for binding 1000+ (large html) objects to the DOM using Rivets. However, the root issue is the fact that I'm trying to bind 1000+ rivets into the DOM at once and that is going to be inherently slow using any technique (I've tested with React, jQuery, Rivets, etc...).

    The solution to this problem is to simply find another way. I've chosen to pre-render the html on the server and serve it up through an API response. This cut the page load time down from ~5s to ~1s.