骨干路由器或视图是否应该处理获取数据并显示加载状态?

Pet*_*ons 17 backbone.js

在我的应用程序的许多地方发生以下模式:

  • 用户单击某些链接触发导航
  • 需要获取数据以呈现视图
  • UI设计需要在获取数据时显示"加载"微调器
  • 获取数据后,我们将显示渲染的视图

我尝试了以下两种实现模式:

  1. 路由器处理提取

    • 路由器告诉容器视图显示加载微调器
    • 路由器加载任何集合/模型
    • 路由器告诉容器视图隐藏加载微调器
    • 路由器将集合/模型传递给视图并呈现它
  2. 查看句柄获取

    • 路由器只是创建并呈现视图
    • 该视图将获取所需的集合和模型
    • 首次渲染视图时,它只显示加载微调器,因为数据仍在加载
    • 当数据到达时,模型/集合触发事件并将视图绑定到那些,以便重新渲染自身,从而隐藏加载微调器并显示完整视图

我不喜欢#1,因为路由器变成了模型/集合获取逻辑的巨大球,并且似乎有太多的责任.#2似乎是一个更好的职责分配(路由器只是决定要显示哪个视图,查看它需要获取哪些数据),但它确实使视图呈现有点棘手,因为它现在是有状态的.

StackOverflow社区的想法是什么?1,2还是其他什么?

小智 23

这篇文章很老了,但是我们今天早些时候正在审查它,所以如果有其他人遇到它:

对我来说,我真的看到两个不同的问题:

  1. 在路由器或视图中,数据提取机制和结果视图呈现应该在哪里发生?
  2. 视图是否期望已经解析的模型,或者它们是否应该响应可能仍在加载的模型?

我们如何处理它与一些个人偏好混合:

  1. 虽然我靠近路由器,但两者都没有.路由器应该处理路由,视图应该处理查看,还有其他东西应该处理模型/集合获取逻辑的机制和工作流.我们将其他东西称为控制器,路由器基本上委托给它.
  2. 正如尤里暗示的那样,"有时"是一个现实.我认为这可能是一个案例决定,但最终应该是Controller和View之间的合同,而不是路由器/视图之间的合同.

我喜欢Yuri的要点,有几个警告(缩进的子弹):

  • 路由器只知道发送用户的位置
  • 外部视图只知道用户应该查看的内容(给定其数据)
    • 假设外部视图特定于内部视图的用例,并由另一个视图"拥有"(用于清理)
    • 否则对于通用容器(比如渲染到'main'位置),我们发现有一个组件可以管理页面上某个'section'的视图 - 我们称之为Renderer
  • 内部视图只知道如何只显示它们的一小部分(并且可以在其他地方使用)
  • 并且渲染函数始终显示正确的内容.
    • 在通用容器的情况下,它最终由渲染器负责

渲染器的主要原因是处理与该部分相关的内容,例如清理现有视图以避免幻影视图,在渲染时滚动到顶部(我们的MainContentRenderer会这样做),或者在这种情况下显示微调器.

一个psuedo-code-ish的例子,可能是这样的:

  • 一般内容目标'main'(如果它是特定于用例的,根据Yuri的示例,使用ComponentView可能会更好,具体取决于您的视图生命周期管理策略)
  • 我们必须获取并等待的模型
  • 接受已加载模型的视图

路由器:

routes: {
    "profile": "showProfile"
},

showProfile: function() {
    return new ProfileController().showProfile();
}
Run Code Online (Sandbox Code Playgroud)

ProfileController可:

showProfile: function() {
    //simple case
    var model = new Model();
    var deferredView = model.fetch.then(function() {
        return new View(model);
    };
    MainContentRenderer.renderDeferred(deferredView);
}
Run Code Online (Sandbox Code Playgroud)

MainContentRenderer:

var currentView;

renderDeferred: function(deferredView) {
    showSpinner();
    deferredView.then(function(view) {
        this.closeSpinner();
        this.closeCurrentView();
        this.render(view);
    }
},

render: function(view) {
    currentView = view;
    $('#main-content').html(view.render().el);
}

closeCurrentView: function() {
    if (currentView and currentView.close()) {
        currentView.close();
    }
}
Run Code Online (Sandbox Code Playgroud)

介绍控制器还具有可测试性的额外好处.例如,我们有复杂的规则来执行URL管理搜索,在结果视图和新搜索视图之间进行选择,以及在缓存的"最后"搜索结果和执行新搜索之间进行选择.我们对控制器进行Jasmine测试,以验证所有流逻辑是否正确.它还提供了一个孤立的地方来管理这些规则.


Yur*_*dow 7

我倾向于使用带有三个视图的第二个选项,即容器,加载视图和内容视图.也就是说,容器由路由器实例化,并且在每次渲染期间,它查看它现有的显示内容 - 有时由路由器提供,有时由自身提供 - 并决定实例化哪些视图.一个简单的,人为的例子:

ContainerView = Backbone.View.extend({

  initialize: function (options) {
    options.data.bind("reset", this.render, this);
  },

  render: function () {
    var view;

    // If the loading view is only shown once, e.g., splashing, then isReady()
    // may be better here.
    if (this.options.data.isLoading()) {
      view = LoadingView;
    } else {
      view = DataView;
    }

    this.$("div.content").html(new view().render().el);
  }

});
Run Code Online (Sandbox Code Playgroud)

我喜欢这种方法,因为:

  • 路由器只知道在那里发送用户;
  • 外视图只知道什么用户应该被观看(给定其数据);
  • 内部视图只知道如何只显示它们的一小部分(并且可以在其他地方使用); 和
  • 渲染函数始终显示正确的东西.

澄清: 在这种情况下,视图的目的是了解如何最好地向用户显示所显示的内容.在这种情况下,仍然可以使用加载视图最好地显示仍然加载的一些数据,而使用数据视图最好地显示就绪数据.大多数真实视图实际上是用更多视图组成它们的显示,例如,取决于用户授权不同的动作容器.