Javascript 主干僵尸视图&;良好做法

Javascript 主干僵尸视图&;良好做法,javascript,backbone.js,Javascript,Backbone.js,我对主干相当陌生,我试图理解僵尸视图的来龙去脉 根据这一点,僵尸是: 当我们通过事件将对象绑定在一起时,我们不必费心解除绑定。只要这些对象绑定在一起,并且在我们的应用程序代码中至少有一个引用,它们就不会被清理或垃圾收集。由此产生的内存泄漏就像电影中的僵尸——躲在黑暗的角落里,等着跳出来吃我们的午餐 上面提到的文章建议创建一个对象来管理视图之间的转换,然后实现一个close函数来移除和解除绑定视图 也就是说,根据情况,从哪里调用关闭函数 我在父视图的initialize块中添加了一个属性,以保留子

我对主干相当陌生,我试图理解僵尸视图的来龙去脉

根据这一点,僵尸是:

当我们通过事件将对象绑定在一起时,我们不必费心解除绑定。只要这些对象绑定在一起,并且在我们的应用程序代码中至少有一个引用,它们就不会被清理或垃圾收集。由此产生的内存泄漏就像电影中的僵尸——躲在黑暗的角落里,等着跳出来吃我们的午餐

上面提到的文章建议创建一个对象来管理视图之间的转换,然后实现一个close函数来移除和解除绑定视图

也就是说,根据情况,从哪里调用关闭函数

我在父视图的initialize块中添加了一个属性,以保留子视图的跟踪。这样我就可以在用新的替换之前调用它的.remove()。这是良好的做法还是有更好的方法

我也不明白为什么定义
el
然后用

this.el.html(this.template(this.model.attributes))

不允许在视图按预期工作时解除绑定,方法是

$('#sportsManDetails').html(this.el.html(this.template(this.model.attributes))

至于示例,我刚刚创建了一个简单的应用程序,它显示运动员姓名列表,并在单击姓名时显示更多详细信息

以下是代码和工作模式:

html

名称视图

app.NameView = Backbone.View.extend({
    tagName: 'li',
    className: 'sportsMan',
    template: _.template($('#nameListTemplate').html()),

    initialize: function(){
        this.sportsManDetailsView;  
    },

    events: {
        'click': 'showSportsManDetails'
    },

    showSportsManDetails: function(e){
        if (typeof this.sportsManDetailsView !== 'undefined'){
            this.sportsManDetailsView.remove();
        }
        this.sportsManDetailsView = new app.SportsManDetailsView({
            model: this.model
        })  
    },

    render: function(){
        this.$el.append(this.template(this.model.attributes));
        return this;
    }
});
app.NameListView = Backbone.View.extend({
    el: '#sportsMenName',

    initialize: function(sportsMen){
        this.collection = new app.SportsMenCollection(sportsMen);
        this.render();
    },

    render: function(){
        this.collection.each(function(sportsMen){
            this.renderContact(sportsMen);
        }, this);
    },

    renderContact: function(sportsMen){
        var nameView = new app.NameView({
            model: sportsMen   
        });
        this.$el.append(nameView.render().el);
    }
});
app.SportsManDetailsView = Backbone.View.extend({
    // doesn't work if I use el in conjunction with 
    // this.$el.html(this.template(this.model.attributes));
    // el: '#sportsManDetails',
    template: _.template($('#sportsManDetailsTemplate').html()),

    initialize: function(){
        this.render();
    },

    events: {
        'click .test': 'test'
    },

    test: function(){
        alert('test');  
    },

    render: function(){                      
        // that does not work
        //this.$el.html(this.template(this.model.attributes));

        // is this good practice?
        $('#sportsManDetails').html(this.$el.html(this.template(this.model.attributes)));
    }
});
名称列表视图

app.NameView = Backbone.View.extend({
    tagName: 'li',
    className: 'sportsMan',
    template: _.template($('#nameListTemplate').html()),

    initialize: function(){
        this.sportsManDetailsView;  
    },

    events: {
        'click': 'showSportsManDetails'
    },

    showSportsManDetails: function(e){
        if (typeof this.sportsManDetailsView !== 'undefined'){
            this.sportsManDetailsView.remove();
        }
        this.sportsManDetailsView = new app.SportsManDetailsView({
            model: this.model
        })  
    },

    render: function(){
        this.$el.append(this.template(this.model.attributes));
        return this;
    }
});
app.NameListView = Backbone.View.extend({
    el: '#sportsMenName',

    initialize: function(sportsMen){
        this.collection = new app.SportsMenCollection(sportsMen);
        this.render();
    },

    render: function(){
        this.collection.each(function(sportsMen){
            this.renderContact(sportsMen);
        }, this);
    },

    renderContact: function(sportsMen){
        var nameView = new app.NameView({
            model: sportsMen   
        });
        this.$el.append(nameView.render().el);
    }
});
app.SportsManDetailsView = Backbone.View.extend({
    // doesn't work if I use el in conjunction with 
    // this.$el.html(this.template(this.model.attributes));
    // el: '#sportsManDetails',
    template: _.template($('#sportsManDetailsTemplate').html()),

    initialize: function(){
        this.render();
    },

    events: {
        'click .test': 'test'
    },

    test: function(){
        alert('test');  
    },

    render: function(){                      
        // that does not work
        //this.$el.html(this.template(this.model.attributes));

        // is this good practice?
        $('#sportsManDetails').html(this.$el.html(this.template(this.model.attributes)));
    }
});
SportsManDetailsView

app.NameView = Backbone.View.extend({
    tagName: 'li',
    className: 'sportsMan',
    template: _.template($('#nameListTemplate').html()),

    initialize: function(){
        this.sportsManDetailsView;  
    },

    events: {
        'click': 'showSportsManDetails'
    },

    showSportsManDetails: function(e){
        if (typeof this.sportsManDetailsView !== 'undefined'){
            this.sportsManDetailsView.remove();
        }
        this.sportsManDetailsView = new app.SportsManDetailsView({
            model: this.model
        })  
    },

    render: function(){
        this.$el.append(this.template(this.model.attributes));
        return this;
    }
});
app.NameListView = Backbone.View.extend({
    el: '#sportsMenName',

    initialize: function(sportsMen){
        this.collection = new app.SportsMenCollection(sportsMen);
        this.render();
    },

    render: function(){
        this.collection.each(function(sportsMen){
            this.renderContact(sportsMen);
        }, this);
    },

    renderContact: function(sportsMen){
        var nameView = new app.NameView({
            model: sportsMen   
        });
        this.$el.append(nameView.render().el);
    }
});
app.SportsManDetailsView = Backbone.View.extend({
    // doesn't work if I use el in conjunction with 
    // this.$el.html(this.template(this.model.attributes));
    // el: '#sportsManDetails',
    template: _.template($('#sportsManDetailsTemplate').html()),

    initialize: function(){
        this.render();
    },

    events: {
        'click .test': 'test'
    },

    test: function(){
        alert('test');  
    },

    render: function(){                      
        // that does not work
        //this.$el.html(this.template(this.model.attributes));

        // is this good practice?
        $('#sportsManDetails').html(this.$el.html(this.template(this.model.attributes)));
    }
});
app.js

var sportsMen = [
    {first: 'Quentin', last: 'Tarant', age: '34', sport: 'bike', category: '- 90kg'},
    {first: 'Aymeric', last: 'McArthur', age: '54', sport: 'jetski', category: '200HP'},
    {first: 'Peter', last: 'TheFat', age: '45', sport: 'curling', category: 'dunno'},
    {first: 'Charles', last: 'Martel', age: '21', sport: 'Moto', category: 'MX 250cc'},
];

$(function(){
    new app.NameListView(sportsMen);
});
如果在创建视图的构造函数时未加载
jQuery
,则可能无法工作

确保在此行之前加载了
jQuery

app.SportsManDetailsView = Backbone.View.extend({
你可以跟我核对一下

console.log(jQuery.fn.jquery);`

正如您所发现的,主干网认为自己更像是一个库,而不是一个框架——它给开发人员留下了许多问题和设计模式

术语“僵尸视图”用于指定当您认为某个视图已经死了时仍然绑定到某个对象(因此是活的)的视图。通常会有一个从model.on call或类似文件中对视图的剩余引用。基本上是一种特定形式的内存泄漏

要管理视图的生命周期,可以使用父视图,但通常从路由器执行此操作。路由器用于删除旧视图,并在发生路由事件时实例化新视图。下面是我如何经常做到这一点的一个片段:

render: function(){
    this.mainView && this.mainView.remove();                    // if there is already a view, remove it
    this.mainView = new SomeOtherKindOfViewDeterminedBySomeEvent(); // instantiate the new view
    this.mainView.render();
    this.mainView.$el.appendTo( '#main-content' );              // append it
}
需要注意的一些事项:

  • 如果不在视图上显式调用
    remove
    ,您的应用程序将容易发生内存泄漏。这是因为视图的事件和属性仍然存在于后台。例如,如果删除上面示例的第一行,我将失去对前面的
    this.mainView
    的引用,但它的事件仍在使用内存。随着时间的推移,这将对你的应用程序产生影响
  • 请注意,我在最后一行中使用了
    appendTo
    。在视图上调用
    remove
    时,它的整个元素及其事件都将被删除。如果我只是这么做:

    this.mainView=newsomeotherkindofview由某个事件({el:'#main content'})决定

    然后,在我调用
    this.mainView
    上的
    remove
    之后,
    #主要内容
    将从DOM中删除,因此我无法再使用该选择器。通过附加它,我将
    #主要内容作为占位符保留,这样我就可以继续向它附加视图这是您在尝试解除绑定时看到的
    SportsManDetailsView
    ,然后再次渲染它。

  • 关于你的问题,这是:

    $('#sportsManDetails').html(this.el.html(this.template(this.model.attributes))

    不是良好做法。首先,您使用了全局jQuery对象,它击败了主干的封装视图方法。其次,从以前的视图来看,事件在DOM中仍然处于活动状态,导致内存泄漏。当您单击测试按钮时可以看到这一点-每当您实例化一个
    SportsManDetailsView
    (第二次,警报消息将显示两次,然后是三次,以此类推)时,处理程序函数都会启动

    您应该依赖父视图或路由器来处理此类交互。或者将您的
    SportsManDetailsView
    绑定到
    #sportsManDetails
    元素,并且永远不要删除它。然后,当您的
    名称视图中发生单击事件时,让其模型触发事件。然后,您的
    SportsManDetailsView
    可以在相应的集合中侦听事件并相应地重新呈现自身拥抱主干网的活动JavaScript是一种事件驱动的语言,永远不要忘记,在您的应用程序中有它们


    我已经更新了您的网站,以展示我所谈论的一些内容。

    这真是太棒了!你在这件事上启发了我。谢谢,没问题!你问了一个很好的问题,值得好好解释一下。祝你旅途好运!我不认为这是完美的,但这是游戏的一部分。再次感谢:)“僵尸视图”是指当您认为它们已经死了时,仍然绑定到某个对象(因此是活的)的视图。通常,在调用或类似调用时,
    模型中有对视图的剩余引用。基本上是一种特定形式的内存泄漏。请随意编辑我的帖子以更正“僵尸视图”的定义,然后添加
    console.log(jQuery.fn.jQuery)
    就在app.SportsManDetailsView=Backbone.View.extend之前({
    输出2.1.1,证明jQuery加载良好…