提问者:小点点

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


在我的应用程序的许多地方,都会发生以下情况:

  • 用户单击某些链接触发导航

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

>

  • 路由器处理获取

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

    获取视图句柄

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

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

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


  • 共2个答案

    匿名用户

    这篇文章很老了,但我们今天早些时候已经看过了,所以万一有人看到它:

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

    1. 数据获取机制和由此产生的视图渲染应该在哪里发生,在路由器还是视图中?
    2. 视图应该期望已经解决的模型,还是应该响应可能仍在加载的模型?

    我们的一些处理方法与一些个人偏好混合在一起:

    1. 都不是,尽管我更靠近路由器。

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

    • 路由器只知道将用户发送到哪里
    • 外部视图只知道用户应该查看什么(给定其数据)
      • 假设外部视图特定于内部视图的用例,并且由另一个视图“拥有”(用于清理)
      • 另外,对于一般容器(如渲染到“主”位置),我们发现使用一个组件来管理页面上某个“部分”的视图非常有用—我们称之为渲染器
      • 在通用容器的情况下,最终是渲染器的责任

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

      一个类似于伪代码的例子,说明了这可能是什么样子:

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

      路由器:

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

      配置文件控制器:

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

      主内容渲染器:

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

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

    匿名用户

    我倾向于将第二个选项用于三个视图:容器视图、加载视图和内容视图。也就是说,容器由路由器实例化,在每次渲染期间,它查看手头上要显示的内容(有时由路由器提供,有时由自身提供),并决定实例化哪个视图。一个过于简单、人为的例子:

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

    我喜欢这种方法,因为:

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

    澄清:在这种情况下,视图的目的是理解如何最好地向用户显示要显示的内容。在这种情况下,仍在加载的数据最好用加载视图显示,而就绪数据最好用数据视图显示。大多数真实视图实际上是用更多的视图组成它们的显示,例如,根据用户授权不同的操作容器。