Backbone.js视图内容保持倍增而不是清除

时间:2012-09-01 20:33:27

标签: backbone.js backbone-views

我是Backbone.js的新手,这个问题真让我难过。

视图是从集合构建的,集合结果被过滤以将每组结果放入它们自己的数组中,然后我从每个数组中创建第一个项目的另一个数组,这些是显示的4个项目。

这在第一次呈现页面时工作正常但是当我离开此页面然后返回页面时,现在有8个项目,每次重新访问页面时,这种添加4的模式都会继续。

    // Locatore List Wrapper
    var LocatorPageView = Backbone.View.extend({

        postshop: [],
        postbox: [],
        postboxlobby: [],
        postboxother: [],
        closestPlaces: [],

        el: '<ul id="locator-list">',

        initialize:function () {        
            this.model.bind("reset", this.render, this);        
        },

        render:function (eventName) {
           //console.log(this)

        // Loop over collecion, assigining each type into its own array
            this.model.models.map(function(item){
                var posttype = item.get('type').toLowerCase();
                switch(posttype) {
                    case 'postshop':                              
                      this.postshop.push(item);
                      break;
                    case 'postbox':
                      this.postbox.push(item);
                      break;
                    case 'postbox lobby':
                      this.postboxlobby.push(item);
                      break;                          
                    default:
                      this.postother.push(item);
                }                     
              return ;
            }, this);   

        // Create a closest Places array of objects from the first item of each type which will be the closest item
            if (this.postshop && this.postshop.length > 0) {
                this.closestPlaces.push(this.postshop[0]);                      
            }
            if (this.postbox && this.postbox.length > 0) {
                this.closestPlaces.push(this.postbox[0]);
            }
            if (this.postboxlobby && this.postboxlobby.length > 0) {
                this.closestPlaces.push(this.postboxlobby[0]);
            }
            if (this.postother && this.postother.length > 0) {
                this.closestPlaces.push(this.postother[0]);
            }

        // Loop over the Closest Places array and append items to the <ul> contianer    
            _.each(this.closestPlaces, function (wine) {
                $(this.el).append(new LocatorItemView({
                    model:wine
                }).render().el);

            }, this);
            return this;
        }

    })

    // Locator single item
    var LocatorItemView = Backbone.View.extend({

        tagName:"li",

        template:_.template($('#singleLocatorTemplate').html()),

        render:function (eventName) {
            $(this.el).html(this.template(this.model.toJSON()));
            return this;
        },

        events: {
            "click .locator-map": "loadMap"
        },

        loadMap: function(e) {

            e.preventDefault();             
            // Instantiate new map
            var setMap = new MapPageView({
                model: this.model,
                collection: this.collection
            });

            var maptype = setMap.model.toJSON().type;
            App.navigate('mappage', {trigger:true,  replace: true});

            setMap.render();
            App.previousPage = 'locator';

        }   


    });

window.App = Backbone.Router.extend({               
    $body: $('body'),
    $wrapper: $('#wrapper'),
    $header: $('#header'),
    $page: $('#pages'),


    routes: {
               '' : '',
         'locator': 'locator'
    },          

    locator:function () {
        this.$page.empty();                                             // Empty Page

        this.places = new LocatorPageCollection();                      // New Collection
        this.placeListView = new LocatorPageView({model:this.places});  // Add data models to the collection

        this.places.fetch();

        this.$page.html(this.placeListView.render().el);                // Append the renderd content to the page
        header.set({title: 'Locator'});                                 // Set the page title
        this.$body.attr('data-page', 'locator');                        // Change the body class name
        this.previousPage = '';                                         // Set previous page for back button

    }

});     

2 个答案:

答案 0 :(得分:2)

Backbone.View.extend参数中的所有属性都附加到视图的原型中。特别是,这些属性:

    postshop: [],
    postbox: [],
    postboxlobby: [],
    postboxother: [],
    closestPlaces: [],

最终附加到LocatorPageView.prototype,因此每个LocatorPageView实例共享同一组数组,每次使用LocatorPageView时,都会将更多内容推送到同一组共享数组中。

如果在Backbone视图中需要任何可变属性(即数组或对象),则必须在构造函数中设置它们:

initialize: function() {
    this.postshop      = [ ];
    this.postbox       = [ ];
    this.postboxlobby  = [ ];
    this.postboxother  = [ ];
    this.closestPlaces = [ ];
}

现在每个实例都有自己的一组数组。

答案 1 :(得分:1)

这听起来像是一个经典的Zombie View问题。基本上当你这样做时:

this.model.bind("reset", this.render, this);

在你看来,你永远不会解开它。因此,视图对象仍然绑定到模型,不能从内存中删除。创建新视图并重置时,您的侦听器仍然处于活动状态,这就是您看到重复视图生成的原因。每次关闭并重做视图时,都会累积监听器,这就是为什么它以4的倍数增加。

当你关闭视图并摆脱你的程序绑定时,你想要做的是unbind你的听众。

this.model.unbind("reset", this.render, this);

这应该消除讨厌的僵尸。当我找到它时,我会添加一个包含更详细信息的链接。

更新 - 添加了有用的参考资料

我也曾经遇到过这个问题。这是Backbone的常见问题。 @Derick Bailey有一个非常好的解决方案,效果很好并且解释得很好。我已经包含了以下链接。查看他在历史中提供的有关此问题的一些答案。他们都读得很好。

Zombies! Run!

Backbone, JS, and Garbage Collection