1

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

问题

我正在构建一个像 CRUD 一样的标准 Rails 界面来跟踪我的约会。我在 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 绑定发生了什么?

我有一个项目的表演活动,我可以来回走动没有问题。我已经比较了两者,它们看起来像是同一种调用。

4

1 回答 1

4

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

问题概述

initialize当您第一次使用路由器的和方法加载该视图index时,一切都很好,因为您有一个新的 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 中删除后留下模型和集合事件绑定,从而导致真正的内存泄漏。如果您决定不重用视图,则需要使替换 #containerhtml 的代码更加健壮,并让它清理旧视图。

我也有一篇博客文章详细介绍了解决方案:http: //lostechies.com/derickbailey/2011/09/15/zombies-run-managing-page-transitions-in-backbone-apps/ - be一定要阅读这篇文章中 Johnny Oshika 的评论,因为他指出了一个非常有用的 StackOverflow 答案,其中他展示了一种处理模型和集合事件的简单方法。

于 2012-04-17T01:45:22.123 回答