在initialize 方法中,您正在尝试同步获取用户,以便立即呈现它们。 las,没有同步请求这样的东西,所以您的视图将呈现一个空集合。在良好的 Backbone 风格中,你需要监听事件所以你知道什么时候是渲染的正确时间:
initialize: function() {
// { async: false } does not do anything, so we may as well
// remove it.
usersC.fetch()
// You can still render immediately, in case the users have
// already been fetched before the view was constructed,
this.render()
// ... but in any case, you probably want to re-render whenever
// the data change (such as when the above request completes).
this.listenTo(usersC, 'update', this.render);
},
接下来的六个问题是错失的遵循最佳实践的机会。这些目前不会破坏您的代码,但它们很可能在未来:
- 您将
usersC设置为LeaderboardDeetsView的model,但它是一个集合。视图同时具有 model 和 collection 属性,因此您应该根据各自的用途使用它们。
- 您正在设置
model(应该是collection)在原型上.虽然这在原则上可行,但您不能使用此机制来拥有多个 LeaderboardDeetsView 实例,每个实例都呈现不同的用户列表(因为它们都共享相同的原型)。出于这个原因,View 构造函数接受一个带有 model 和 collection 属性的选项对象,因此您可以为每个视图提供自己独特的模型:
var LeaderboardDeetsView = Backbone.View.extend({
el: $('#bbcontent'),
initialize: function() {
this.collection.fetch()
this.render()
this.listenTo(this.collection, 'update', this.render);
},
render: function() {
var self = this;
this.collection.each(function(c) {
var block = "<div class='name'><h1>" + c.get('username') + "</h1></div>"
self.$el.append(block)
})
}
})
var leaderboardDeetsView = new LeaderboardDeetsView({
collection: usersC,
});
- 在几个地方,您没有用分号 (
;) 结束您的语句。大多数时候,JavaScript 会让你摆脱这种情况,但并非总是如此。训练自己严格遵守这一点,并避免一些不愉快和令人困惑的意外发生!
- 在 MVC 范例中,视图不应该决定何时获取数据,除非它是为了响应用户操作(在这种情况下视图扮演控制器的角色)。在您的情况下,由于您希望在启动应用程序后立即获取数据,因此对
fetch 的调用属于视图之外。
- 在
LeaderboardDeetsView 的类定义中,您将el 设置为已解析的jQuery 实例。它在这种情况下工作正常,但在一般情况下,具有给定选择器的元素可能还不存在。将 el 设置为一个选择器字符串,视图将在构建视图时自动为您执行此查找。
- 按照约定,视图的
render 方法应返回 this,以便您可以在它之后继续链接方法。大多数其他尚未返回其他值的方法也是如此。考虑到目前的所有问题,您的代码现在应该如下所示:
//model
var User = Backbone.Model.extend({
idAttribute: "userId",
defaults: {
username: null,
userLevel: null
}
});
//collection
var Users = Backbone.Collection.extend({
model: User,
url: "/CW2/ASSWDCW2/cw2app/index.php/Leaderboard/leaderboard",
});
var usersC = new Users();
// Fetch the collection right after construction.
usersC.fetch();
var LeaderboardDeetsView = Backbone.View.extend({
// Selector string will be jQuery-wrapped automatically.
el: '#bbcontent',
initialize: function() {
// We can chain listenTo after render because of "return this".
this.render().listenTo(this.collection, 'update', this.render);
},
render: function() {
var self = this;
this.collection.each(function(c) {
var block = "<div class='name'><h1>" + c.get('username') + "</h1></div>";
self.$el.append(block);
});
// This last line enables chaining!
return this;
}
});
var leaderboardDeetsView = new LeaderboardDeetsView({
collection: usersC,
});
最后三个问题错失了从可用的最新最好的库中获益的机会:
- 您使用的是非常过时的 jQuery、Underscore 和 Backbone 版本。这些都是非常稳定的库,因此您可以受益于七年多的错误修复、性能提升和与现代浏览器的改进兼容性,所有这些都无需更改代码中的单个字符!
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.3/jquery.min.js">
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.13.6/underscore-
min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.4.1/backbone-min.js">
</script>
- 为集合中的每个模型呈现相同的东西是几乎每个 Web 应用程序都需要做的事情。当然有些图书馆可以为您简化这项工作。下面,我展示了如何使用我编写的小型库 backbone-fractal 重写
LeaderboardDeetsView。或者,您可以使用 Marionette 中的 CollectionView(但在这种情况下语法不同)。这使您的代码更加模块化、更易于理解以及更易于测试和维护。
// A simple, dedicated view for a single entry in the leaderboard.
var UserView = Backbone.View.extend({
className: 'name',
initialize: function() { this.render(); },
render: function() {
this.$el.html('<h1>' + c.get('username') + '</h1>');
return this;
}
});
// A simple view for the entire leaderboard, all automatic.
var LeaderboardDeetsView = BackboneFractal.CollectionView.extend({
el: '#bbcontent',
subview: UserView,
initialize: function() {
this.initItems().render().initCollectionEvents();
}
});
- 使用模板生成 HTML 代码,而不是将字符串与手写的 JavaScript 代码连接起来。这使得负责生成 HTML 的代码更易于阅读和编辑。如果您想保持便宜,可以使用 Underscore 的内置
template function。如果您想更认真地对待您的模板,您还可以使用专用模板库,例如Handlebars 或Wontache。下面,我演示了 Underscore 的 _.template 如何适用于上一点的 UserView:
var UserView = Backbone.View.extend({
className: 'name',
// The template: a declarative description of the HTML you want to
// generate.
template: _.template('<h1><%- username %></h1>'),
initialize: function() { this.render(); },
render: function() {
// Using the template. Conventional notation.
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
这是代码的最终版本,实现了上述所有要点。是不是看起来时尚、简洁和模块化?
var User = Backbone.Model.extend({
idAttribute: "userId",
defaults: {
username: null,
userLevel: null
}
});
var Users = Backbone.Collection.extend({
model: User,
url: "/CW2/ASSWDCW2/cw2app/index.php/Leaderboard/leaderboard",
});
var usersC = new Users();
usersC.fetch();
var UserView = Backbone.View.extend({
className: 'name',
template: _.template('<h1><%- username %></h1>'),
initialize: function() { this.render(); },
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
var LeaderboardDeetsView = BackboneFractal.CollectionView.extend({
el: '#bbcontent',
subview: UserView,
initialize: function() {
this.initItems().render().initCollectionEvents();
}
});
var leaderboardDeetsView = new LeaderboardDeetsView({
collection: usersC,
});