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

时间:2012-05-29 23:48:26

标签: backbone.js

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

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

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

  1. 路由器处理提取

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

    • 路由器只创建并呈现视图
    • 视图提取所需的集合和模型
    • 首次呈现视图时,它只显示加载微调器,因为数据仍在加载
    • 当数据到达时,模型/集合触发事件并将视图绑定到那些,以便重新渲染自身,从而隐藏加载微调器并显示完整视图
  3. 我不喜欢#1,因为路由器变成了模型/集合获取逻辑的巨大球,并且似乎有太多的责任。 #2似乎是一个更好的职责分配(路由器只是决定要显示哪个视图,查看它需要获取哪些数据),但它确实使视图渲染变得有点棘手,因为它现在是有状态的。

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

2 个答案:

答案 0 :(得分:23)

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

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

  1. 在路由器或视图中,数据提取的机制和生成的视图呈现应该在哪里?
  2. 视图是否应该预期已经解析的模型,或者它们是否应该响应可能仍在加载的模型?
  3. 我们如何处理它与某些个人偏好相结合:

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

      • 路由器只知道将用户发送到何处
      • 外部视图只知道用户应该查看的内容(给定其数据)
        • 假设外部视图特定于内部视图的用例,并由另一个视图“拥有”(用于清理)
        • 否则对于通用容器(比如渲染到'main'位置),我们发现有一个组件管理页面上某个'section'的视图很有用 - 我们称之为Renderer
      • 内部观点只知道如何只显示它们的一小部分(和 可以在其他地方使用)
      • 和渲染功能总是显示正确的东西。
        • 对于通用容器,它最终由Renderer负责

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

      psuedo-code-ish示例,可能是:

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

      路由器:

      routes: {
          "profile": "showProfile"
      },
      
      showProfile: function() {
          return new ProfileController().showProfile();
      }
      

      ProfileController:

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

      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();
          }
      }
      

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

答案 1 :(得分: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);
  }

});

我喜欢这种方法,因为:

  • 路由器只知道发送用户的 where ;
  • 外部视图只知道用户应该查看的 (给定其数据);
  • 内部视图只知道如何只显示它们的所有小部分(并且可以在其他地方使用);和
  • 渲染功能始终显示正确的内容。

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