Backbone / Marionette获取大量集合会导致浏览器冻结

时间:2014-10-31 16:06:42

标签: backbone.js marionette

我在CompositeView中有一个Marionette,当集合提取时可能会从API中提取1000条记录。我看到的问题是,当UI加载时,浏览器会退出并且脚本会冻结浏览器。

我已尝试关注此博文(http://lostechies.com/derickbailey/2011/10/11/backbone-js-getting-the-model-for-a-clicked-element/),但似乎无法弄清楚如何将其转换为Marionette

这是我的CompositeView

var EmailsMenuView = Marionette.CompositeView.extend({
    template: _.template(templateHTML),

    childView: EmailItemView,

    childViewOptions: function(model, index){
        return {
            parentIndex: index,
            parentContainer: this.$el.closest('form')
        };
    },

    childViewContainer: '.emails',

    ui: {
        emails: '.emails',
        options: '.email-options',
        subject: "#subject_line",
        fromName: "#from_name",
        fromEmail: "#from_email",
        thumbnail: '.thumbnail img',
        emailName: '.thumbnail figcaption'
    },

    events: {
        'change @ui.subject': 'onSubjectChange',
        'change @ui.fromName': 'onFromNameChange',
        'change @ui.fromEmail': 'onFromEmailChange'
    },

    childEvents: {
        'menu:selected:email': 'onMenuEmailSelect'
    },

    collectionEvents: {
        'change:checked': 'onEmailSelected'
    },

    behaviors: {
        EventerListener: {
            behaviorClass: EventerListener,
            eventerEvents: {
                'menu:next-click': 'onNext',
                'menu:previous-click': 'onPrev'
            }
        },
        LoadingIndicator: {
            behaviorClass: LoadingIndicator,
            parentClass: '.emails'
        }
    },

    renderItem: function(model) {
        console.log(model);
    },

    /**
     * Runs automatically during a render
     * @method EmailsMenuView.onRender
     */
    onRender: function () {
        var colCount, showGridList, selected,
            threshold = 300;

        if(this.model.get('id')) {
            selected = this.model;
        }

        // refresh its list of emails from the server
        this.collection.fetch({
            selected: selected,
            success: function (collection) {
                colCount = collection.length;
                console.log(colCount);
            },
            getThumbnail: colCount <= threshold
        });

        this.collection.each(this.renderItem);

        var hasEmail = !!this.model.get('id');
        this.ui.emails.toggle(!hasEmail);
        this.ui.options.toggle(hasEmail);
        eventer.trigger(hasEmail ? 'menu:last' : 'menu:first');
    }
});

我的ItemView看起来像这样:

var EmailItemView = Marionette.ItemView.extend({
        tagName: 'article',

        template: _.template(templateHTML),

        ui: {
            radio: 'label input',
            img: 'figure img',
            figure: 'figure'
        },

        events: {
            'click': 'onSelect',
            'click @ui.radio': 'onSelect'
        },

        modelEvents: {
            'change': 'render'
        },

        behaviors: {
            Filter: {
                behaviorClass: Filter,
                field: "email_name"
            }
        },

         /**
         * initializes this instance with passed options from the constructor
         * @method EmailItemView.initialize
         */
        initialize: function(options){
            this.parentIndex = options.parentIndex;
            this.parentContainer = options.parentContainer;

            this.listenTo(eventer, 'menu:scroll', this.onScroll, this);
        },

        /**
         * Runs automatically when a render happens. Sets classes on root element.
         * @method EmailItemView.onRender
         */
        onRender: function () {
            var checked = this.model.get("checked");
            this.$el.toggleClass('selected', checked);
            this.ui.radio.prop('checked', checked);
        },

        /**
         * Runs after the first render, only when a dom refrsh is required
         * @method EmailItemView.onDomRefresh
         */
        onDomRefresh: function(){
            this.onScroll();
        },

        /**
         * Marks this item as checked or unchecked
         * @method EmailItemView.onSelect
         */
        onSelect: function () {
            this.model.collection.checkSingle(this.model.get('id'));
            this.trigger('menu:selected:email');
        },

        templateHelpers: {
            formattedDate: function() {
                var date = this.updated_ts.replace('@D:', '');
                if (date !== "[N/A]") {
                    return new Moment(date).format('dddd, MMMM DD, YYYY [at] hh:mm a');
                }
            }
        },

        /**
         * Delays the load of this view's thumbnail image until it is close to
         * being scrolled into view.
         * @method EmailItemView.onScroll
         */
        onScroll: function () {
            if (this.parentIndex < 10) {
                // if it's one of the first items, just load its thumbnail
                this.ui.img.attr('src', this.model.get('thumbnail') + '?w=110&h=110');
            } else if (this.parentContainer.length && !this.ui.img.attr('src')) {
                var rect = this.el.getBoundingClientRect(),
                    containerRect = this.parentContainer[0].getBoundingClientRect();
                // determine if element is (or is about to be) visible, then
                // load thumbnail image
                if (rect.top - 300 <= containerRect.bottom) {
                    this.ui.img.attr('src', this.model.get('thumbnail') + '?w=110&h=110');
                }
            }
        }
    });

我正在尝试调整CompositeView以便在我可以构建HTML的位置工作,然后渲染缓存的HTML而不是附加1000个元素

1 个答案:

答案 0 :(得分:0)

渲染所有1000个元素并不是个好主意 - DOM会变得太大。此外,在获取时,所有1000个模型都有解析,并且它可以同步工作。所以有两种方法 - 加载较少的数据,或splist数据来解析/渲染块