Backbone.js: undelegateEvents 不删除事件

Backbone.js: undelegateEvents not removing events

在以下代码中:

HTML

<div id="myView">
  <button id="test_button">
    Test Button
  </button>
  <ul id="output"></ul>
</div>

JavaScript

var myView = Backbone.View.extend({

    initialize: function() {
        // why doesn't this remove the previously delegated events?
        this.undelegateEvents();

        this.delegateEvents({
            'click #test_button': 'buttonClicked'
        });
    },

    // this event fires twice for one button click    
    buttonClicked: function() {
        $("#output").append('<li>Button was clicked</li>'); 
    }
});

$(document).ready(function(){
    new myView({el: "#myView"});    

  // instantiate view again
  new myView({el: "#myView"});  
});

为什么

this.undelegateEvents();

在 Backbone View 的 initialize() 方法中没有从 View 的先前实例化中删除先前委托的事件?

以上代码的JSFiddle示例:https://jsfiddle.net/billb123/o43zruea/28/

我会尽量不大喊大叫,但请停止尝试将视图绑定到现有元素。让视图创建并拥有自己的 el,然后在替换它之前调用 view.remove() 将其杀死。这个简单的更改解决了视图事件的许多问题,如果您不这样做,您应该三思而后行。

在你的情况下,你会 HTML 像这样:

<script id="t" type="text/x-underscore">
  <div id="myView">
    <button id="test_button">
      Test Button
    </button>
  </div>
</script>
<div id="container">
</div>
<ul id="output"> <!-- This is outside the container because we're going to empty and refill it -->
</ul>

你的 JavaScript 看起来像这样:

var myView = Backbone.View.extend({
  events: {
    'click #test_button': 'buttonClicked'
  },
  render: function() {
    this.$el.html($('#t').html());
    return this;
  },
  buttonClicked: function() {
    $("#output").append('<li>Button was clicked</li>'); 
  }
});

$(document).ready(function(){
  var v = new myView();
  $('#container').append(v.render().el);

  v.remove(); // <----------------- Clean things up before adding a new one

  v = new myView();
  $('#container').append(v.render().el);
});

兴趣点:

  1. 创建视图然后渲染它然后将它放在页面上。
  2. 完成视图后调用 remove
  3. 视图进入容器内部。调用者拥有容器,视图拥有其 el.
  4. 任何地方都没有 delegateEventsundelegateEvents 呼叫。这些的存在几乎总是指向您的应用程序 IMO 中的结构问题。
  5. 每个视图都是自包含的:外部世界不会玩弄视图内部的任何东西,视图也不会影响自己。

已更新 fiddle:https://jsfiddle.net/bp8fqdgm/


但为什么您的尝试 undelegateEvents 没有任何作用? undelegateEvents looks like this:

undelegateEvents: function() {
  if (this.$el) this.$el.off('.delegateEvents' + this.cid);
  return this;
},

cid 每个视图实例都是唯一的,因此每个视图实例都使用自己唯一的命名空间来处理 delegateEvents 绑定的事件。这意味着:

this.undelegateEvents();
this.delegateEvents();

是说:

  1. 删除此视图实例绑定的事件。这些事件将在 '.delegateEvents' + this.cid 命名空间中找到,其中 cid 对于每个视图实例都是唯一的。
  2. 绑定此视图实例定义的事件(或 delegateEvents 调用中的事件)。这些事件将使用 '.delegateEvents' + this.cid 命名空间附加。

因此您的 undelegateEvents 调用正在删除事件但不是全部,只有视图 实例 添加的特定事件绑定被删除。

您的 this.undelegateEvents() 调用实际上没有完成任何事情,因为它在错误的地点和错误的时间调用。如果 new View 呼叫者进行了 undelegateEvents 呼叫:

var v = new myView({el: "#myView"});    
v.undelegateEvents();
new myView({el: "#myView"});

那么它就会在正确的时间正确的地点发生。当然,这意味着您的路由器需要跟踪当前视图,以便它可以在正确的时间currentView.undelegateEvents();但如果你这样做,那么你最好(IMO)采用我在答案顶部概述的方法。