32

我在几个地方读到调用 Backbone.history.navigate 函数被认为是不好的做法。

例如,Addy Osmani sais 在他的“开发 Backbone.js 应用程序”一书中

Router.navigate() 也可以通过传递 trigger:true 选项来触发路由以及更新 URL 片段。注意:不鼓励这种用法...

http://addyosmani.github.io/backbone-fundamentals/#backbone.history

或者 Derick Bailey 在他的博客文章中甚至说:

大多数时候,您不应该在应用程序中执行路由的处理程序。

但我并不真正理解其背后的原因以及更好的解决方案。

在我看来,使用 trigger:true 选项调用导航功能还不错。路由函数可以在调用时始终检查所考虑的数据是否已经加载并显示这个加载的数据,而不是重新做整个工作......

4

4 回答 4

25

Router#navigate我认为,对于究竟做什么似乎有些困惑。

如果没有设置任何选项,它会将 URL 更新为提供的片段。例如,router.navigate('todo/4/edit')URL 更新#todo/4AND 将为该 URL创建一个浏览器历史记录条目没有运行路由处理程序

但是,设置trigger:true会更新 URL,但它也会运行为该路由指定的处理程序(在 Addy 的示例中,它将调用 routerseditTodo函数)并创建浏览器历史记录条目。

传递replace:trueurl 时将更新,不会调用任何处理程序,但不会创建浏览器历史记录条目

那么,我认为答案是:

不鼓励使用 的原因trigger:true很简单,从应用程序状态导航到应用程序状态到应用程序状态与直接导航到特定应用程序状态相比,大多数情况下需要运行不同的代码。

假设您的应用程序中有状态 A、B 和 C。但是状态 B 建立在状态 A 之上,而状态 C 建立在 B 之上。在这种情况下,当您从 B 导航到 C 时,只需要执行特定部分的代码,而当直接到达状态 C 时,可能会执行一些状态检查和准备:

  • 数据是否已加载?如果没有,请加载它。
  • 用户登录了吗?如果不重定向。

等等

举个例子:State A ( #list) 显示歌曲列表。状态 B ( #login) 是关于用户认证,状态 C ( #list/edit) 允许编辑歌曲列表。

因此,当用户到达状态 A 时,歌曲列表被加载并存储在一个集合中。他单击登录按钮并被重定向到登录表单。他成功验证并被重定向回歌曲列表,但这次歌曲旁边有删除按钮。

他为最后一个状态添加了书签 ( #list/edit)。

现在,当用户在几天后点击书签时需要发生什么?应用程序需要加载歌曲,需要验证用户(仍然)登录并做出相应的反应,状态转换流程中的内容已经在其他状态下完成。

现在来做一个我自己的笔记:

我绝不会像示例中那样在实际应用程序中推荐上述方法。您应该检查从 B 到 C 时是否加载了集合,而不仅仅是假设它已经加载。同样,您应该检查用户是否确实已登录。这只是一个示例。

IMO 路由器确实是一种特殊的视图(想想看,它显示应用程序状态并将用户输入转换为应用程序状态/事件)并且应该始终这样对待。您永远不应该依赖路由器在状态之间转换,而是让路由器反映状态转换。

于 2013-07-23T09:38:43.613 回答
11

我不得不在这里不同意@Stephen 的回答。主要原因是因为使用router.navigate({trigger : true})赋予路由器处理应用程序状态的责任。它应该只反映应用程序状态,而不是控制它。

View此外,更改窗口的哈希值不是 a的责任,这是路由器的唯一工作!不要把它拿走!良好的模块化和关注点分离使得应用程序具有可扩展性和可维护性。

将人员转发到应用程序中的新部分

Backbone 是一个事件驱动的框架,使用事件进行通信。绝对不需要调用router.navigate({ trigger : true }),因为功能不应该在路由器中。这是我如何使用路由器的示例,我认为可以促进良好的模块化和关注点分离。

var Router = Backbone.Router.extend({
  initialize: function(app) {
    this.app = app;
  },
  routes: {
    'videoLibrary' : function() { this.app.videoLibrary(); }
  }
});

var Application = _.extend({}, Backbone.Events, {
  initialize: function() {
    this.router = new Router( this );
    this.listenTo( Backbone, 'video:uploaded', function() { 
      this.router.navigate('/videoLibrary');
      this.videoLibrary();
    });
  },
  videoLibrary: function() {
    //do useful stuff
  }
});

var uploadView = Backbone.View.extend({
  //...
  uploadVideo: function() {
    $.ajax({
      //...
      success: function() { Backbone.trigger('video:uploaded'); }
    });
  }
});

您的视图不需要或不想知道用户完成上传后要做什么,这是其他人的责任。在这个例子中,路由器只是应用程序功能的一个入口点,由它生成的事件uploadView是另一个。路由器始终通过散列更改和历史记录反映应用程序状态,但不实现任何功能。

可测试性

通过分离关注点,您正在增强应用程序的可测试性。Backbone.trigger监视并确保视图正常工作很容易。模拟路由器不太容易。

模块管理

此外,如果您使用 AMD 或 CommonJS 之类的模块管理,则必须在应用程序的任何地方传递路由器实例才能调用它。因此,在您的应用程序中具有紧密耦合,这不是您想要的。

于 2013-07-22T23:20:54.120 回答
3

在我看来,这被认为是不好的做法,因为您应该将 Backbone 应用程序想象成不像 Ruby On Rails 应用程序,而更像是桌面应用程序。

当我说 RoR 时,我只是在说一个支持路由的框架,因为路由将您带到对控制器的特定调用以运行特定操作(想象一下 CRUD 操作)。

Backbone.history 仅用作用户的书签,例如,他可以保存特定的 url,并稍后再次运行它。在这种情况下,他会发现他之前离开的情况。

当你说:

在我看来,使用 trigger:true 选项调用导航功能还不错。路由函数可以在调用时始终检查所考虑的数据是否已经加载并显示这个加载的数据,而不是重新做整个工作......

这对我来说听起来很臭。如果您正在触发一个路由并且您正在检查数据以查看您是否拥有它,这意味着您实际上已经拥有它们,因此您应该相应地更改您的视图,而无需再次加载具有相同数据的整个 DOM。

那就是说trigger:true我们有理由使用它吗?在我看来,如果您完全交换视图,则可以使用它。

假设我有一个带有两个选项卡的应用程序,一个允许我创建单个资源,另一个允许我查看已创建资源的列表。在第二个选项卡中,您实际上正在加载一个集合,因此两者之间的数据不同。在这种情况下,我会使用trigger:true.

也就是说,我已经使用 Backbone 两周了,所以我对这个世界还很陌生,但对我来说,不鼓励使用这个选项听起来很合理。

于 2013-07-22T21:32:28.343 回答
1

这取决于您的上下文。

如果您在当前视图中做了一些可能会影响您将要导航到的视图的操作,例如创建删除客户记录,那么将 trigger 设置为 true 是正确的做法。

想想看。如果您删除客户记录不想刷新客户列表以反映该删除?

于 2015-02-18T22:42:18.753 回答