【问题标题】:Backbone events are firing multiple times after re-rendering sub-views重新渲染子视图后,主干事件会触发多次
【发布时间】:2013-07-23 23:08:47
【问题描述】:

我们有一个由侧边栏和几个子视图组成的 Backbone 视图。为简单起见,我们决定让侧边栏和子视图由单个 render 函数管理。然而,click .edit 事件似乎在单击侧边栏项目之一后触发了多次。例如,如果我从“常规”开始并单击.edit,则hello 会触发一次。如果我然后单击侧边栏上的.profile 并再次单击.edit,hello 会触发两次。有什么想法吗?

查看

events: {
  "click .general": "general",
  "click .profile": "profile",
  "click .edit": "hello",
},

general: function() {
  app.router.navigate("/account/general", {trigger: true});
},

profile: function() {
  app.router.navigate("/account/profile", {trigger: true});
},

render: function(section) {
  $(this.el).html(getHTML("#account-template", {}));
  this.$("#sidebar").html(getHTML("#account-sidebar-template", {}));
  this.$("#sidebar div").removeClass("active");
  switch (this.options.section) {
    case "profile":
      this.$("#sidebar .profile").addClass("active");
      this.$("#content").html(getHTML("#account-profile-template"));
      break;
    default:
      this.$("#sidebar .general").addClass("active");
      this.$("#content").html(getHTML("#account-general-template"));
  }
},

hello: function() {
  console.log("Hello world.");
},

路由器

account: function(section) {
  if (section) {
    var section = section.toLowerCase();
  }
  app.view = new AccountView({model: app.user, section: section});
},

解决方案

我的解决办法是把路由器改成这样:

account: function(section) {
  if (section) {
    var section = section.toLowerCase();
  }
  if (app.view) {
    app.view.undelegateEvents();
  }
  app.view = new AccountView({model: app.user, section: section});
},

这暂时可行,但这会造成内存泄漏吗?

【问题讨论】:

  • 如果您发布您的路由器代码,我可以验证您正在创建重复视图实例的预感。
  • 好的,我把路由器的相关部分贴出来了。

标签: javascript backbone.js


【解决方案1】:

当我第一次开始使用骨干网时,我遇到了完全相同的问题。就像彼得说的那样,问题在于您创建了多个视图实例并监听事件。为了解决这个问题,我在上一个主干项目中创建了这个解决方案:

/* Router view functions */
showContact:function () {
    require([
        'views/contact'
    ], $.proxy(function (ContactView) {
        this.setCurrentView(ContactView).render();
    }, this));
},
showBlog:function () {
    require([
        'views/blog'
    ], $.proxy(function (BlogView) {
        this.setCurrentView(BlogView).render();
    }, this));
},


/* Utility functions */
setCurrentView:function (view) {
    if (view != this._currentView) {
        if (this._currentView != null && this._currentView.remove != null) {
            this._currentView.remove();
        }
        this._currentView = new view();
    }
    return this._currentView;
}

如您所见,它总是删除最后一个视图并创建一个新视图,然后再进行渲染。我还在路由器中添加了一条 require 语句,因为我不想在真正需要它们之前加载路由器中的所有视图。祝你好运。

【讨论】:

  • 谢谢!很高兴看到我不是唯一处理这个问题的人。这是一个很好的解决方案。
  • @j-a-x 你能告诉更多关于你如何安排路由器和应用程序文件。
  • @jeevs 对不起,好久没碰骨干了。我现在进入 Angular 了 :)
【解决方案2】:

听起来您将多个视图实例附加到同一个 DOM 元素并且它们都在响应事件。您是否在每次导航时都创建一个新视图而不删除之前的视图?

【讨论】:

  • 你可能是对的,虽然我只是在创建新视图之前尝试了app.view.remove(),但是新视图根本不渲染......
  • 好吧,你需要渲染它并附加它。您的undelegateEvents 我认为 会好的,但我认为这不是适用于您的用例的最简单的主干模式。
  • 我绝对愿意接受建议 ;-)
  • 我认为路由器操作视图的基本模式应该是构造、渲染、附加、删除、冲洗、重复。但是 Backbone 很灵活,所以做你认为合理的事情。
【解决方案3】:

我有一个动态视图,它根据路由器参数在同一个元素(大约 12 个)内呈现不同的模板。现在,视图呈现的容器在 view.render() 中定义,如“el: '#some-container'”。自然,如果视图存在,我必须在创建新的或相同的视图之前删除它,以防止僵尸和 s#!t。提醒一下,调用 view.remove() 实际上会从 DOM 中删除“#some-container”,这意味着除了第一次之外,视图没有地方可以渲染。现在,有几十种方法可以防止这种情况发生。只是想我应该分享一下,以防有人需要节省几个小时的研究时间。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多