Backbone.js 主干路由器或视图是否应处理获取数据和显示加载状态?
在我的应用程序的许多地方,都会发生以下情况:Backbone.js 主干路由器或视图是否应处理获取数据和显示加载状态?,backbone.js,Backbone.js,在我的应用程序的许多地方,都会发生以下情况: 用户点击一些链接触发导航 需要提取数据以呈现视图 UI设计要求在获取数据时显示“加载”微调器 获取数据后,我们将显示渲染视图 我尝试了以下两种实现模式: 路由器处理抓取 路由器告诉容器视图显示加载微调器 路由器加载任何集合/模型 路由器告诉容器视图隐藏加载微调器 路由器将集合/模型传递给视图并渲染它 视图句柄获取 路由器只是创建和渲染视图 视图获取所需的集合和模型 第一次渲染视图时,它仅显示加载微调器,因为数据仍在加载 当数据到达时,模型/
- 用户点击一些链接触发导航
- 需要提取数据以呈现视图
- UI设计要求在获取数据时显示“加载”微调器
- 获取数据后,我们将显示渲染视图
- 路由器告诉容器视图显示加载微调器
- 路由器加载任何集合/模型
- 路由器告诉容器视图隐藏加载微调器
- 路由器将集合/模型传递给视图并渲染它
- 路由器只是创建和渲染视图
- 视图获取所需的集合和模型
- 第一次渲染视图时,它仅显示加载微调器,因为数据仍在加载
- 当数据到达时,模型/集合将触发事件,视图将绑定到这些事件,以便重新呈现自身,从而隐藏加载微调器并显示完整视图
StackOverflow社区是怎么想的?1、2或其他什么?我倾向于将第二个选项与三个视图一起使用,即容器、加载视图和内容视图。也就是说,容器由路由器实例化,在每次渲染期间,它查看手头上要显示的内容(有时由路由器提供,有时由自身提供),并决定实例化哪个视图。一个过于简单、人为的例子:
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);
}
});
我喜欢这种方法,因为:
- 路由器只知道将用户发送到哪里李>
- 外部视图只知道用户应该查看什么(给定其数据)李>
- 内部视图只知道如何显示它们的一小部分(并且可以在其他地方使用);及
- 渲染函数始终显示当前正确的内容
在这种情况下,视图的目的是理解如何最好地向用户显示要显示的内容。在这种情况下,仍在加载的数据最好用加载视图显示,而就绪数据最好用数据视图显示。大多数真实视图实际上都包含了更多视图,例如,根据用户授权不同的操作容器。这篇文章很老了,但我们今天早些时候已经对它进行了回顾,所以如果其他人看到它: 对我来说,我确实看到了两个不同的问题:
- 路由器只知道将用户发送到哪里
- 外部视图只知道用户应该查看什么(给定其数据)
- 假设外部视图特定于内部视图的用例,并且由另一个视图“拥有”(用于清理)
- 另外,对于一般容器(如渲染到“主”位置),我们发现使用一个组件来管理页面上某个“部分”的视图非常有用—我们称之为渲染器
- 内部视图只知道如何显示它们的一小部分(以及 可在其他地方使用)
- 并且渲染函数始终显示当前正确的内容。
- 对于通用容器,最终将由渲染器负责
- 通用内容目标“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();
}
}
配置文件控制器:
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();
}
}
MainContentRenderer:
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测试,以验证所有流逻辑是否正确。它还提供了一个独立的位置来管理这些规则。您编写的
isLoading
和isReady
函数也是这样吗?假设您将选项.data
向下传递到数据视图