Backbone.js导航工作一次但不是再次

时间:2012-04-17 01:15:34

标签: backbone.js coffeescript

我正在使用Backbone-on-rails gem中的Backbone.js 0.9.2。我也试图使用新的'pushState'而不是旧的哈希URL。

问题

我正在构建一个标准的Rails,如CRUD界面,以跟踪我的约会。我在主index.jst.eco页面上有一个“新”链接:

<h1>Appointments</h1>
<p><a href="/appointments/new" class="new">New Appointment</a></p>

我加载页面并单击该“新”链接并且主干会触发事件,而不必重新加载整个页面。这是事件:

class BackboneOnRails.Views.AppointmentsIndex extends Backbone.View
  template: JST['appointments/index'],
  events: ->
    'click .new': 'newAppointment'

  newAppointment: ->
    Backbone.history.navigate("/appointments/new", {trigger: true})
    return false

  # The rest of the index methods omitted for brevity 

然后调用骨干路由器:

class BackboneOnRails.Routers.Appointments extends Backbone.Router
  routes:
    '': 'index'
    'appointments': 'index'
    'appointments/new': 'new'

  initialize: ->
    this.appointments = new BackboneOnRails.Collections.Appointments()
    this.appointmentsIndexView = new BackboneOnRails.Views.AppointmentsIndex({collection: this.appointments})
    this.appointmentsIndexView.render()

  index: ->
    $("#container").html(this.appointmentsIndexView.el)
    this.appointments.fetch()

  new: ->
    appointments = new BackboneOnRails.Collections.Appointments()
    view = new BackboneOnRails.Views.AppointmentNew({collection: appointments})
    $("#container").html(view.render().el)

当我点击浏览器后退按钮时会出现问题,然后再次尝试使用“新建”链接。这一次,它完全重新加载页面。

当我回浏浏览器时,javascript绑定会发生什么?

我有一个项目的展示活动,我可以来回走动没问题。我比较了两者,它们看起来像是同一种呼叫。

1 个答案:

答案 0 :(得分:4)

问题在于您尝试重新使用appointmentmentsIndexView实例。从DOM中删除视图会破坏DOM事件处理程序。将视图的el重新添加到DOM不会重新连接它们。

问题概述

当您第一次使用路由器的initializeindex方法加载该视图时,一切都很好,因为您有一个新的IndexView实例。 DOM事件正确附加到视图中,生活也很好。

当您点击路由器的new路由/方法时,您实际上是在尝试从屏幕上删除索引视图并将其替换为添加新视图。这从视觉角度和添加新视图的角度起作用。

但是,当您点击后退按钮时,您将停留在浏览器选项卡中的同一个实时应用程序实例中。点击启用了pushstate的后退按钮会告诉浏览器不要重新加载整个应用程序,只是为了更新URL并触发索引的路由器方法。

在这种情况下,您的索引视图不会从头开始重建。您正在重新使用相同的视图实例,但使用服务器中的数据重新加载它。数据加载完全正常,因为您的视图和集合仍然附加。但是,DOM事件绑定失败,因为它们先前已删除绑定并且未重新添加。

2常用解决方案

有两种常见的解决方案,以及这些解决方案的许多变体。

1)不要重复使用视图实例。

这是我强烈建议的建议。在我试图重新使用视图实例的每个实例中,我一直遇到很大的问题 - 包括你遇到的确切问题。

相反,每次需要显示约会索引时,重新创建一个新的视图实例。这意味着您在路由器的index方法中创建了新的索引视图,而不是initialize方法。

2)清除并重新绑定DOM事件

如果由于某种原因,你觉得你真的需要重新使用视图实例(绝不应该这样),你可以用Tim Branyen在他的博客上发布的一些信息来解决它:

http://tbranyen.com/post/missing-jquery-events-while-rendering

我不推荐这种方法。重新使用视图实例可能看起来是一个好主意,但它会导致其他问题的不良路径,包括在应用程序中留下太多未使用的部分来臃肿内存使用。

旁注:僵尸和内存泄漏

在任何一种情况下 - 无论您是决定重用视图实例还是在需要时重新创建它们 - 您可能会遇到一些内存泄漏。

在重新使用视图的情况下,当您不需要时,您将明确地保留在内存中的对象。这不是真正的“泄漏”,而是过度使用内存。您应该在不需要时取消引用该对象,并在需要时重新创建它。这将减少内存使用量并使您的应用程序更好地运行。

我有一篇关于其工作原理的博客文章:http://lostechies.com/derickbailey/2012/03/19/backbone-js-and-javascript-garbage-collection/

在不重复使用视图的情况下,通过在从可见DOM中删除视图后保留模型和集合事件绑定,可能会导致真正的内存泄漏。如果您决定不重复使用您的观点,则需要使代替#container html的代码更加健壮,并让它清理旧视图。

我有一篇博文,详细说明了解决方案:http://lostechies.com/derickbailey/2011/09/15/zombies-run-managing-page-transitions-in-backbone-apps/ - 请务必阅读Johnny Oshika在这篇文章中的评论,因为他指出了一个非常有用的StackOverflow答案处理模型和集合事件的简单方法。