骨干僵尸的意见和放大器;好习惯 [英] Backbone zombie views & good practice

查看:89
本文介绍了骨干僵尸的意见和放大器;好习惯的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是相当新的骨干,我试着去了解的僵尸观点的来龙去脉。

一个僵尸,根据本<一个href=\"http://lostechies.com/derickbailey/2011/09/15/zombies-run-managing-page-transitions-in-backbone-apps/\"相对=nofollow>文章:


  

当我们绑在一起,通过事件的对象,但我们不要打扰他们解除绑定。只要这些对象被结合在一起,并且在我们的应用程序code键它们中的至少一个参考,它们将不被清理或回收的垃圾。由此产生的内存泄漏是喜欢看电影的僵尸 - 躲在阴暗的角落里,等待着跳出来,吃我们的午餐


以上mentionned文章建议创建管理视图之间的过渡,然后实现接近函数删除和解除绑定的视图中的对象。

如此说来,根据不同的情况,在那里打电话从关功能?

我在父视图的初始化块添加属性保住孩子视图的痕迹。这样,我能够调用一个.remove()在它之前,我通过更换一个新的。它是很好的做法还是有一个更好的办法?

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

渲染

这$ el.html(this.template(this.model.attributes));

不允许我解除绑定的观点,而它的工作原理是这样的预期。

<$c$c>$('#sportsManDetails').html(this.$el.html(this.template(this.model.attributes)));

至于为例,我刚刚创建了一个简单的应用程序,显示的运动员的名字列表和显示更多的细节上的名字时,点击

这里的code和工作小提琴

HTML

 &LT;脚本ID =nameListTemplate类型=文/模板&GT;
    &LT;%=首%GT; &LT;%=最后%GT;
&LT; / SCRIPT&GT;
&LT;脚本ID =sportsManDetailsTemplate类型=文/模板&GT;
    &LT; UL&GT;
        &LT;立GT;&LT;%=首%GT;&LT; /李&GT;
        &LT;立GT;&LT;%=最后%GT;&LT; /李&GT;
        &LT;立GT;&LT;%=年龄%GT;&LT; /李&GT;
        &LT;立GT;&LT;%=体育运动%GT;&LT; /李&GT;
        &LT;立GT;&LT;%=类别%GT;&LT; /李&GT;
    &LT; / UL&GT;
    &LT;按钮类=测试&GT;测试与LT; /按钮&GT;
&LT; / SCRIPT&GT;
&LT; D​​IV ID =sportsMenName&GT;&LT; / DIV&GT;
&LT; D​​IV ID =sportsManDetails&GT;&LT; / DIV&GT;

JS

模型和收集

  VAR应用程序= || {};app.SportsManModel = Backbone.Model.extend({});app.SportsMenCollection = Backbone.Collection.extend({
    型号:app.SportsManModel
});

NameView

  app.NameView = Backbone.View.extend({
    标签名:礼,
    产品类别:'型男',
    模板:_.template($('#nameListTemplate')HTML()),    初始化:功能(){
        this.sportsManDetailsView;
    },    事件:{
        '点击':'showSportsManDetails
    },    showSportsManDetails:功能(E){
        如果(typeof运算this.sportsManDetailsView!==未定义){
            this.sportsManDetailsView.remove();
        }
        this.sportsManDetailsView =新app.SportsManDetailsView({
            型号:this.model
        })
    },    渲染:功能(){
        这$ el.append(this.template(this.model.attributes));
        返回此;
    }
});

NameListView

  app.NameListView = Backbone.View.extend({
    EL:#sportsMenName',    初始化:功能(运动员){
        this.collection =新app.SportsMenCollection(运动员);
        this.render();
    },    渲染:功能(){
        this.collection.each(功能(运动员){
            this.renderContact(运动员);
        }, 这个);
    },    renderContact:功能(运动员){
        VAR nameView =新app.NameView({
            型号:运动员
        });
        这$ el.append(nameView.render()EL);
    }
});

SportsManDetailsView

  app.SportsManDetailsView = Backbone.View.extend({
    //如果我结合使用EL与不工作
    //这个$ el.html(this.template(this.model.attributes))。
    // EL:#sportsManDetails',
    模板:_.template($('#sportsManDetailsTemplate')HTML()),    初始化:功能(){
        this.render();
    },    事件:{
        点击.TEST':'测试'
    },    测试:功能(){
        警报('测试');
    },    渲染:功能(){
        //不工作
        //this.$el.html(this.template(this.model.attributes));        //这是很好的做法?
        $('#sportsManDetails)HTML(这$ el.html(this.template(this.model.attributes)));
    }
});

app.js

  VAR运动员= [
    {第一:昆汀',最后:Tarant',年龄:34,运动:自行车,类别:' - 90千克},
    {第一:艾默里克',最后:麦克阿瑟,年龄:'54',运动:滑水,类别:'200HP},
    {第一:'彼得',最后:TheFat',年龄:'45',运动:'冰壶',类别:不知道},
    {第一:'查尔斯',最后:马特尔',年龄:'21',运动:'摩托',类别:MX 250cc组},
];$(函数(){
    新app.NameListView(运动员);
});


解决方案

正如你发现,骨干认为自己多了一份的不是框架 - 它留下了很多问题和设计模式留给了开发者。我没有听说过这个词僵尸观点,但它听起来像是打算调解其他视图之间的交互的视图。这并不一定是一个视图 - 更多的时候把设备用在路由事件来删除旧的意见和实例化新的。这里有一个如何我经常做到这一点的代码段:

 渲染:功能(){
    this.mainView&功放;&安培; this.mainView.remove(); //如果已经有一个观点,将其删除
    this.mainView =新SomeOtherKindOfViewDeterminedBySomeEvent(); //实例化新观点
    this.mainView.render();
    this.mainView $ el.appendTo('#主要内容)。 //追加它
}

需要注意以下几点:


  1. 如果没有显式调用删除在视图上,您​​的应用程序将是脆弱的内存泄漏。这是因为查看的事件和属性的背景仍然存在。例如,如果您删除上面的例子中的第一行,我会失去我参考了以前的 this.mainView ,但它的事件仍在使用的内存。这会对你的应用的效果随着时间的推移。

  2. 请注意,我使用 appendTo 中的最后一行。打电话时删除在View,它的整个元素被删除,以及它的事件。如果我只是做到了这一点:

    this.mainView =新SomeOtherKindOfViewDeterminedBySomeEvent({EL:'#主内容'})

    我打电话再经过删除 this.mainView #主含量将被从DOM中删除,这样我就可以不再使用该选择。通过追加它,我保持这种#主含量周围作为占位符,这样我就可以继续追加的意见吧。 这是试图解除 SportsManDetailsView 然后再渲染它时,你所看到的。


至于你的问题,这样的:

<$c$c>$('#sportsManDetails').html(this.$el.html(this.template(this.model.attributes)));

时的不可以很好的做法。这首先是你已经使用了全局jQuery对象,这违背了封装的观点骨干的做法。其次,事件仍然在来自前视图的DOM活性,导致内存泄漏。你可以看到这个当你点击测试按钮 - 处理函数将火每次实例化一个 SportsManDetailsView (第二次左右,警报消息将显示两次时,然后三次等)

您应该依靠你的僵尸视图来处理这种相互作用。这,或让你的 SportsManDetailsView 绑定到 #sportsManDetails 元素,从来没有将其删除。然后,当点击事件发生在你的 NameView ,有它的模型触发触发一个事件。那么你的 SportsManDetailsView 可以监听相应的集合中的事件,并相应地重新呈现自身。 拥抱骨干的活动! JavaScript是一种事件驱动的语言,永远不要忘记,你有那些在您的火炮。

我已经更新您的的jsfiddle 来证明一些什么我已经讲过了。

I'm fairly new to backbone and I try to understand the ins and outs of zombie views.

A zombie is, according to this article:

When we bind objects together through events but we don’t bother unbinding them. As long as these objects are bound together, and there is a reference in our app code to at least one of them, they won’t be cleaned up or garbage collected. The resulting memory leaks are like the zombies of the movies – hiding in dark corners, waiting to jump out and eat us for lunch.

The article mentionned above suggests to create an object that manages the transitions between views and then to implement a close function to remove and unbind the view.

That being said, depending on the situation, where to call that close function from?

I add a property in the initialize block of my parent view to keep a trace of the child view. That way I'm able to call .remove() on it before I replace it by a new one. Is it good practice or is there a better way?

I also don't understand why defining el and then rendering with

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

doesn't allow me to unbind the view while it works as expected by doing

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

As for the exemple, I just created a simple app that displays a list of sportsmen's names and that shows more details when clicking on a name.

Here's the code and a working fiddle:

html

<script id="nameListTemplate" type="text/template">
    <%= first %> <%= last %>
</script>
<script id="sportsManDetailsTemplate" type="text/template">
    <ul>
        <li><%= first %></li>
        <li><%= last %></li>
        <li><%= age %></li>
        <li><%= sport %></li>
        <li><%= category %></li>
    </ul>
    <button class="test">Test</button>
</script>
<div id="sportsMenName"></div>
<div id="sportsManDetails"></div>

JS

model and collection

var app = app || {};

app.SportsManModel = Backbone.Model.extend({});

app.SportsMenCollection = Backbone.Collection.extend({
    model: app.SportsManModel
});

NameView

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;
    }
});

NameListView

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);
    }
});

SportsManDetailsView

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);
});

解决方案

Just as you're discovering, Backbone considers itself more of a library than a framework - it leaves a lot of questions and design patterns left to the developer. I've not heard the term "zombie view" before, but it sounds like a view intended to mediate interaction between other views. This doesn't necessarily have to be a view - more often the router is used to remove old views and instantiate new ones upon a route event. Here's a snippet of how I often accomplish this:

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
}

Some things to note:

  1. Without explicitly calling remove on a view, your app will be vulnerable to memory leaks. This is because the View's events and properties still exist in the background. For example, if you remove the first line of the example above, I will lose my reference to the former this.mainView, but it's events are still using memory. This will have an effect on your app over time.
  2. Note that I'm using appendTo in the last line. When calling remove on a View, it's entire element is removed, as well as it's events. If I had simply done this:

    this.mainView = new SomeOtherKindOfViewDeterminedBySomeEvent({ el: '#main-content' })

    Then after I call remove on this.mainView, #main-content will have been removed from the DOM, so I can no longer use that selector. By appending it, I keep that #main-content around as a placeholder, so I can continue to append views to it. This is what you are seeing when trying to unbind SportsManDetailsView then render it again.

As for your questions, this:

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

Is not good practice. This first is that you've used the global jQuery object, which defeats Backbone's approach of encapsulated views. Second, the events are still active in the DOM from former views, leading to memory leaks. You can see this when you click the Test button - the handler function will fire for every time you instantiated a SportsManDetailsView (the second time around, the alert message will be shown twice, then three times, etc.)

You should rely on your zombie view to handle such interaction. That, or keep your SportsManDetailsView bound to the #sportsManDetails element, and never remove it. Then when the click event occurs in your NameView, have its model trigger trigger an event. Then your SportsManDetailsView can listen for the event in the corresponding collection and re-render itself accordingly. Embrace Backbone's events! JavaScript is an event-driven language, and never forget that you have those in your artillery.

I have updated your JSFiddle to demonstrate some of what I've talked about.

这篇关于骨干僵尸的意见和放大器;好习惯的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆