tbranyen/backbone.layoutmanager — pull request #354 by ssafejava (2013-07-24 01:25:54+00:00)
Title: Batch render list views by adding to a docFragment

This addresses issue #270 (and discussion on #345) and produces a moderate speedup on modern browsers and (I assume) a major speedup on legacy browsers. When the benchmark module is completed as @tbranyen has previously mentioned we can quantify this better.

By default, views with many subviews render in the following fashion:

* ParentView: render template
* forEach subView: render template. If async, wait for template to load before rendering next view (#261)
* forEach subView: attach to parent
* ParentView: attach to parent if present

So essentially, the render chain goes all the way down to the lowest depth and attaches to parents on the way up. This works more or less fine on initial renders, as all attachments are done in memory until the root element(s) are attached to the document. The problem occurs during rerenders, where subviews are written to the DOM one at a time.

By default, views now have an option `{useFragment: true}`. This produces the following flow:

* ParentView: render template
* forEach subView: mark {insertedViaFragment: true}, render in parallel (instead of in serial as in #261. Will speed up any render chains with async fetches)
* ParentView: build docFragment, insert all subViews, append fragment
* ParentView: attach to parent if present

This PR preserves options like `{insert: Function}` on subViews (see `htmlBatch()`) with a single caveat: subViews cannot escape their parents. This PR therefore passes all tests but one: `'insertView' uses user-defined 'insert' method`. While this rendering approach can indeed use user-defined insert methods, the process of building a fragment then appending it *inside* the parent is not compatible with the `insert` function defined, which inserts a subView before its parent.

I don't believe that is a valid test; if one wants to create a sibling view, it should not be done by creating a subView and overriding `insert()`.

In terms of performance, in my testing I see about a 20% speedup in Chrome on list renders of 20 items, most of which comes from the elimination of a GC event that usually occurs in the middle of a traditional insert. Chrome seems rather intelligent about batching layout actions until all writes are completed. I can't speak to any performance improvements in IE or mobile browsers as I don't have the hardware to test.

Before merging this I think we should discuss the validity of the single failing test & produce a benchmark or two. @tbranyen, you said you were doing some work on that; do you have any sample benchmarks I can work from?