Javascript Backbonejs:正在传递父对象';s元素引用子视图是一种好的做法吗?

Javascript Backbonejs:正在传递父对象';s元素引用子视图是一种好的做法吗?,javascript,backbone.js,view,Javascript,Backbone.js,View,在提供父视图和子视图时,我需要两件事: 子视图应在实例化时呈现自身 子级的渲染方法应该知道当前父级的dom元素 实际上,从父视图,而不是此视图: ,add_bannerbox_view:function(model, collection, options){ var bannerbox = new BannerBoxView({ model: model }); this.bannerbox_views[model.cid] = bannerbox; this.ba

在提供父视图和子视图时,我需要两件事:

  • 子视图应在实例化时呈现自身
  • 子级的渲染方法应该知道当前父级的dom元素
实际上,从父视图,而不是此视图:

,add_bannerbox_view:function(model, collection, options){
    var bannerbox = new BannerBoxView({ model: model });
    this.bannerbox_views[model.cid] = bannerbox;
    this.bannerbox_container.append(bannerbox.el);
    bannerbox.render();
}
我只想这样

    ,add_bannerbox_view:function(model, collection, options){
        //here, BannerBoxView is supposed to render itself from initialize() 
        this.bannerbox_views[model.cid] = new BannerBoxView({ model: model, parent:this.el });
    }

但我想知道:将父母的元素传递给孩子是一种好的做法吗?还是它有一些不好的缺点?

我部分地回答自己。除了循环引用(我只传递一个dom元素),我希望在child的render()方法中使用的自附加功能可能会出现缺点。原因是当有大量视图时可能会发生内存泄漏。这里有一个很好的解释:

我应该在父视图中使用
var container=document.createDocumentFragment()
,然后可能将容器传递给子视图

另外,在上面的讨论之后,我仍然不完全相信各种观点(我的第一点:p),我正在使用某种桥接代码。现在,我喜欢这样做:我不将父级的dom元素作为构造函数参数传递。相反,我将它直接传递给子对象的render()。代码被彻底清除:

//parent
var CustomBannersView = Backbone.View.extend({

initialize:function(){
    this.groups_container = $('.groups-container');
    this.group_views = {};
    this.init();
    this.set_events();
}

,init:function(){
    //instantiate views without rendering for later use
    this.collection.each(function(model){
        this.group_views[model.cid] = new GroupView({ model:model, id:'group-' + model.cid });
    },this);
}

,render:function(){
    var temp_box = document.createDocumentFragment();

    //render views without dom refresh. Passing the box.
    _.each(this.group_views, function(groupview){ groupview.render(temp_box); });

    //add container
    this.groups_container.append(temp_box);
}

//dom events ----
,events:{
    'click .create-gcontainer-button': function(){
        this.collection.add(new Group());
    }
}

,set_events:function(){
    this.listenTo(this.collection,'add',function(model, collection, options){           
        //render a single subview, passing the main container
        //no refresh problem here since it's a single view
        this.group_views[model.cid] = new GroupView({ model: model, id:'group-' + model.cid }).render(this.groups_container);
    });
}
});//end view


//child
var GroupView = Backbone.View.extend({
  tagName: 'fieldset'
  ,className: 'group'

  ,initialize:function(){
    this.template = Handlebars.compile($('#group-container').html()); 
  }

  ,render:function(box){//box passed by parent
    this.$el.html(this.template(this.model.toJSON()));
    $(box).append(this.$el);
    //now I can set things based on dom parent, if needed
    return this;
  }
});

松耦合几乎总是优于紧耦合。我能想到的两个最好的理由是:

  • 可重复使用。可供应用程序中的任何位置使用,而无需担心依赖关系
  • 可测试的。可独立于其他部件进行测试
通过要求子视图引用父视图,可以促进紧密耦合,即子视图依赖于父视图。这使得可重用性非常困难,如果您正在编写单元测试,那么您将不得不实例化或模拟父类,以便测试子类。这是不必要和乏味的

如果您真正想做的是让子视图自动渲染,只需扩展核心
主干.view
,并包含父视图可以调用的辅助函数

var MyView = Backbone.View.extend({
    renderChild: function(view, options) {
        var childView = new view(options);
        this.views[options.model.cid] = childView;
        this.$el.append(childView.el);
        childView.render();
    }
});
然后,您可以这样定义父视图:

var ParentView = MyView.extend({
    add_bannerbox_view: function() {
        this.renderChild(BannerBoxView, {model: model});
    }
});

我们制作的helper函数将允许您使用一行代码实例化、附加和呈现子视图。

根据您的尝试,更好的方法可能是使用事件将两者解耦,例如,与您的子视图在其父元素上呈现某些内容不同,它只会触发一个事件,父视图将侦听该事件并相应地执行操作。好的,是的,我已经将主干的事件用作其他事件的事件路由器。不过,在本例中,是父对象创建子对象。因此,如果我使用一个事件来尝试解耦2,我想我应该将父元素作为事件的回调参数传递给子元素,如上所述。我这里真正的问题可能是,如何让孩子知道将自己附加到dom的位置。为什么不让父视图将孩子的视图附加到dom?(
this.$('#someElement').append(this.bannerbox_views[model.cid].el);
)这正是我试图避免的事情之一,因为这样做,我还需要调用子元素的render()方法,因为它需要知道父元素进行某些计算,所以在追加元素之前无法调用它。无论如何,我的主要问题是:这种做法有一些严重的缺点吗?我看到的主要缺点是,你的孩子的观点与你父母的观点更紧密地结合在一起,你可能有一个循环引用。关于渲染视图,虽然一般来说模式是渲染视图,然后附加它,但我不明白为什么您可以反转该模式并执行类似操作(
this.$(“#someElement”).append(view.el);view.render();
)。好的,这是一种很好的技术,谢谢。但是您确定传递父级的$el(不是父级本身)是耦合2吗?我所做的只是简单地传递一个jquery对象(甚至是dom),而不是视图,只是告诉子对象将自身附加到哪里(因为子对象的某些属性需要知道父对象)。至于单元测试,任何应该是子视图的视图都需要附加到某个地方,因此您无论如何都需要某种父视图(不一定是视图,只是一个简单的dom元素)。这怎么样?从技术上讲,您是正确的——传递对DOM元素的引用与传递对父视图本身的引用不同。然而,这是在假设任何视图都可以访问DOM树的任何部分——我认为这违背了模块化设计的原则。我确实觉得DOM树应该得到与我们对其余代码同样的模块化考虑。这是我喜欢HTML5中阴影DOM的原因之一。它强烈鼓励在DOM树中进行模块化开发。