Вопрос: Сортировка/фильтрация в Backbone?

Лучший ответ:
Вы все верно предложили. Чаще всего только так и можно сделать. Главное в DOM вставлять сразу всю отрендеренную коллекцию, а не по одной вьюшке. И не забывать подчищать за собой в случае, если есть какие то лихие биндинги.

Худший ответ:
Я считаю что полностью заново рендерить коллекцию при сортировке это вообще шаг назад от идеологии MVC. рендерить заново надо только то что изменилось.
При сортировке или фильтрации, мы можем использовать умное обновление коллекций с сервера. Оно будет вызывать соответствующие события add, remove, change. На любой списочный интерфейс я завожу 4 "класса", например: CommentListViewClass, CommentItemViewClass, CommentCollectionClass, CommentModelClass. 
CommentCollection передаем в CommentListView, и вызываем CommentCollection.fetch() 
Собственно вся соль делается в CommentListView, его и прифожу.var CommentsListViewClass = Backbone.View.extend({
        initialize: function(options){
            this.templateData = options && options.templateData || {};
            this.listenTo(this.collection, "add", this.renderItem);
            this.listenTo(this.collection, "add remove sync", this.renderUpdate);
            this.listenTo(this.collection, "sync", this.resortItems);
            
            this.itemViews = [];
        },
        render: function(){
            this.$el.empty();
        },
        renderUpdate: function(){
            $('.comments_count').html(this.collection.length);
        },
        resortItems: function(){
            this.collection.each(_.bind(function(m,i){
                var viewEl = this.$("."+m.cid);
                this.placeItem(viewEl, m);
            }, this));
        },
        placeItem: function(itemEl, model){
            var index = _.indexOf(this.collection.models, model);
            //console.log(index, itemEl,model);
            if(index == 0){
                console.log("#"+ index ,model.id, 'ontop');
                this.$el.prepend(itemEl);
            } else {
                var pIndex = index-1;
                var cid = this.collection.models[pIndex].cid;
                this.$el.find('.'+cid).after(itemEl);
                console.log("#"+ index, model.id, "after " + this.collection.models[pIndex].id );
            }
        },
        renderItem: function(model, collection, options){
            var item = new CommentsItemViewClass({
                model: model,
                templateData: this.templateData
            });
            
            var itemEl = item.render();
            this.placeItem(itemEl, model);
        }
    });
При добавлении новой модели в коллекцию, мы инициализируем новый вид элемента списка, передавая ему эту же модель. Это делает метод render_item. var item = new CommentsItemViewClass({
                model: model,
                templateData: this.templateData
            });
            //получим DOM-элемент вида
            var itemEl = item.render();
Далее у каждого DOM-элемента (this.el) созданного вида добавляется класс (или любой другой атрибут) с cid той модели которую он отображает (это нужно сделать в шаблоне элемента). Затем нужно определить индекс (позицию) модели в коллекции, и разместить этот элемент под элементом вида предыдущей модели в коллекции (ну или воткнуть на самый верх если таковой не найден), этим далее и занимается метод placeItem . var index = _.indexOf(this.collection.models, model);
            //console.log(index, itemEl,model);
            if(index == 0){
                this.$el.prepend(itemEl);
            } else {
                var pIndex = index-1;
                //cid предыдущей модели
                var cid = this.collection.models[pIndex].cid;
                this.$el.find('.'+cid).after(itemEl);
            }
Аналогично при полном обновлении коллекции после sync, только пробежаться надо по всем моделям коллекции, дабы расставить все элементы по местам. Это уже resortItems.this.collection.each(_.bind(function(m,i){
                var viewEl = this.$("."+m.cid);
                this.placeItem(viewEl, m);
            }, this));