本文概述
- 错误1:忽略Backbone.js功能库
- 错误2:直接响应任意事件时修改DOM
- 错误三:低估渲染成本
- 错误4:让事件监听器无法使用
- 错误5:创建整体视图
- 错误6:未意识到Backbone.js可以适应非RESTful API
- 错误7:在视图上而不是模型中存储数据
- 错误8:使用jQuery” .on()”代替委托事件
- 总结
Backbone.js是一个简约的框架, 旨在提供一组简单的数据结构和功能, 可用于创建结构化Web应用程序的前端。开箱即用的Backbone.js组件提供了一个直观的环境, 你在后端上使用模型和视图时可能已经很熟悉。 Backbone.js中的模型和集合很简单, 但是它们具有一些非常有用的功能, 例如可以轻松地将它们与REST JSON API集成的选项。但是它们也足够灵活, 可以适应几乎所有实际用途。
在本Backbone.js教程中, 我们将介绍一些自由开发人员在尝试学习Backbone.js时经常犯的一些常见错误以及避免这些错误的方法。
错误1:忽略Backbone.js功能库
Backbone.js可能是一个极简主义的框架, 但它(与Underscore.js一起)提供了许多特性和功能, 可以轻松满足开发现代Web应用程序时出现的最基本和某些关键需求。初学者开发人员经常犯的一个常见错误是, 他们将Backbone.js用作Web的另一个类似于MVC的客户端框架。尽管本节讨论的是非常明显的内容, 但是当涉及Backbone.js时, 如果不彻底探索该框架是一个非常关键的错误。该框架的大小可能很小, 但是这正是进行全面研究的理想之选。特别是其小巧且带有注释的源代码。
Backbone.js提供了使Web应用程序能够从中受益的最低要求。凭借其可扩展性和众多的插件, 学习Backbone.js可用于构建一些出色的Web应用程序。 Backbone.js的一些最明显的功能是通过模型, 集合和视图公开的。路由器和历史记录组件提供了一种简单而优雅的机制来支持客户端路由。尽管Underscore.js是Backbone.js的依赖项, 但它已很好地集成到框架中, 因为模型和集合都从JavaScript的这一出色工具带中受益匪浅, 并且也可以使用。
该框架的源代码编写得非常好, 并带有注释, 以至于人们在喝一杯咖啡时都可以阅读全部内容。初学者可以从阅读源注释中受益匪浅, 因为他们可以学到很多有关框架内部工作方式的知识, 并且可以在JavaScript方面采用一套简洁的最佳实践。
错误2:直接响应任意事件时修改DOM
刚开始学习Backbone.js时, 我们倾向于做的事情是不做Backbone.js建议的事情。例如, 我们倾向于像在简单网站上使用jQuery那样处理事件和查看更新。 Backbone.js旨在通过适当的关注点分离为你的Web应用程序提供僵化的结构。我们通常倾向于使用Backbone.js来更新视图以响应任意DOM事件:
var AudioPlayerControls = Backbone.View.extend({
events: {
‘click .btn-play, .btn-pause’: function(event) {
$(event.target).toggleClass(‘btn-play btn-pause’)
}
}, // ...
})
这是应不惜一切代价避免的事情。在可能有意义的地方, 可能会拿出一些晦涩的例子。但在大多数情况下, 还有更好的方法可以做到。实际上, 在这里我可以举例说明的一种方法是使用模型来跟踪音频播放器的状态, 并使用该状态信息来呈现按钮(或更具体地说, 其类名):
var AudioPlayerControls = Backbone.View.extend({
events: {
‘click .btn-play, .btn-pause’: function(event) {
this.model.set(‘playing’, !this.model.get(‘playing’))
}
}, initialize: function() {
this.listenTo(this.model, ‘change’, this.render)
this.render()
}, // ...
})
<button class="btn btn-<%- playing ? ‘pause’ : ‘play’ %>"></button>
在少数情况下, 从事件处理程序直接操作DOM是有意义的, 但是从事件处理程序管理复杂的DOM操作所涉及的成本几乎是不值得的。这是Backbone.js旨在解决的问题。使用Backbone.js做类似的事情是一个错误。
错误三:低估渲染成本
由于Backbone.js使得随意渲染或重新渲染DOM或响应事件变得非常容易, 因此我们常常忽略了这对Web应用程序整体性能的影响。我们最终可以通过多种方法在视图上使用render方法。随着现代Web浏览器正成为高性能的软件, 通常这似乎并不多。但是随着Web应用程序的增长, 以及处理的数据量的增长, 性能下降变得越来越明显。
我们可以通过一个人为设计的示例看到这一点, 我们从一小部分模型开始, 然后将其呈现到列表视图中:
var AudioPlayerPlaylist = Backbone.View.extend({
template: _.template(‘<ul> <% _.each(musics, function(m) { %> <li><%- m.title %></li> <% }) %> </ul>’), initialize: function() {
this.listenTo(this.collection, ‘add’, this.render)
}, // ...
})
在此Backbone.js示例中, 只要将模型添加到集合中, 我们都将重新渲染。这样就可以了。但是, 由于每次将模型添加到列表时都会触发”添加”事件, 因此可以想象从服务器获取大量模型。将连续多次调用render方法, 对于服务器响应中的每个模型一次。足够大的模型足以使你的应用程序停顿并破坏用户体验。有时, 小的响应就足够了, 这取决于要渲染的视图的复杂性。
一个非常快速的解决方案是不为每个要添加的模型调用render方法。在这种情况下, 将批量添加模型, 并且实际上你可以做一些事情来使render方法仅在调用但未在指定的时间内重新调用时触发。 Backbone.js的依赖项Underscore.js随附了一个方便的实用程序功能:” _。debounce”。你需要利用这一点的所有方法是使用以下命令更改事件绑定JavaScript行:
this.listenTo(this.collection, ‘add’, _.debounce(_.bind(this.render), 128))
这将导致每次”添加”事件发生时都会触发事件回调, 但是, 它将从上一个事件开始等待128毫秒, 然后才实际调用render方法。
在大多数情况下, 这将被视为类似于快速修复的解决方案。实际上, 有更适当的方法可以避免渲染抖动。 Trello背后的开发人员曾经写过一篇博客文章, 讨论了他们在使用Backbone.js时改善渲染性能的经验和方法。
错误4:让事件监听器无法使用
不管使用哪种JavaScript框架, 或者根本不使用一个JavaScript框架, 将未使用的事件侦听器都绑定在一起可能会发生。即使Backbone.js可以轻松避免此问题, 但在Web应用程序中仍然留下潜在的漏洞以防止内存泄漏无疑是一个错误。 Backbone.js的”事件”组件无疑是一个非常简洁的实现。它允许JavaScript对象轻松实现基于事件的功能。由于视图是我们大多数事件消费通常发生的地方, 因此很容易在此犯下错误:
var AudioPlayerControl = Backbone.View.extend({
initialize: function() {
this.model.on(‘change’, _.bind(this.render, this))
// ...
}, // ...
})
此代码段中的事件绑定行与第一个示例中的事件绑定行没有太大区别。我们在这里所做的就是将” this.listenTo(this.model, …)”更改为” this.model.on(…)”。由于我们非常习惯于使用其他JavaScript框架和库进行事件绑定的” .on()”调用, 因此当我们开始使用Backbone.js时, 我们通常倾向于使用” .on()”调用进行绑定事件。只有当我们不再需要调用事件处理程序时, 才费心调用” .off()”来解除绑定它们时, 这才是好的。但是我们很少这样做, 最终导致内存泄漏。
Backbone.js提供了一种解决此问题的简单方法。它是通过使用” object.listenTo()”方法来实现的。这使你正在调用” listenTo()”的对象可以跟踪其正在侦听的事件, 还可以轻松地一次取消绑定所有这些事件。例如, 一旦在视图上调用” remove()”, 视图就会自动停止监听所有绑定的事件。
错误5:创建整体视图
如果你考虑一下, Backbone.js的极简主义为你设计Web应用程序的前端提供了极大的灵活性。由于模型, 集合和视图是组件的组成部分, 因此必须使它们尽可能轻巧且尽可能具体。通常, 就代码而言, 视图最终成为Web应用程序中最繁重的方面。但是, 非常重要的是, 不要使最终的巨型视图最终无法完成应用程序必须提供的所有功能。与其将所有逻辑塞入其中, 而不必制作一个巨大的” AudioPlayer”视图, 而是将其拆分为多个逻辑视图, 例如播放列表视图, 控件视图, 可视化器视图等等。你要确保哪种粒度取决于你尝试构建的应用程序。
这是因为对于细粒度视图(每个视图执行特定的操作并正确执行), 使用Backbone.js开发Web应用程序变得轻而易举。你的代码应更具可维护性, 并且将来易于扩展或修改。然后是另一个极端, 你最终会过度使用它。 Backbone.js视图旨在使你能够方便地使用模型或集合, 并且这可能可以提示你如何构建应用程序。伊恩·斯托姆·泰勒(Ian Storm Taylor)在他的博客上分享了一些有价值的想法, 你在实现视图时可能应该牢记。
错误6:未意识到Backbone.js可以适应非RESTful API
Backbone.js可直接使用基于JSON的RESTful API。你只需要jQuery(或可以替代它的东西, 例如Zepto)即可。但是, Backbone.js具有极好的可扩展性。实际上, Backbone.js甚至可以改编为使用其他类型的API和其他类型的编码格式。
Backbone.js涉及前端与后端服务交互的组件是”同步”。该组件提供了许多属性, 你可以轻松覆盖这些属性, 以自定义Backbone.js与API端点进行交互的方式。实际上, 也可以用至少可以说不是很传统的东西来代替默认的同步机制, 例如使用localStorage来持久化数据, 而不是后端服务。
现有许多插件, 可轻松自定义Backbone.js的同步行为。例如, 名为Backbone.dualStorage的插件可让你同时使用后端服务和localStorage来保留数据。当你的应用程序脱机时, 该插件使用localStorage来保留来自缓存数据的请求, 并跟踪你可能在联机时与服务器同步的更改。
尽管将Backbone.js与后端设计为RESTful并与之兼容的后端更易于使用, 但这并不意味着Backbone.js可以使用所有功能。通过对默认的Backbone.js同步机制进行一些更改, 你可以使其适应各种后端服务API和编码格式。
值得一提的是, Backbone.js的其他部分也很灵活, 并且是可选的。例如, 你不必使用Underscore.js随附的默认模板引擎。你甚至不必使用Backbone.js的view组件, 并且可以根据需要将其完全替换为其他组件。
错误7:在视图上而不是模型中存储数据
作为初学者学习Backbone.js时, 我们经常会犯的一个错误是将数据直接存储在视图上作为属性。此数据可能在那里以跟踪某些状态或某些用户选择。这是应该避免的事情。
var AudioPlayerVisualizer = Backbone.View.extend({
events: {
‘click .btn-color’: function(event) {
this.colorHex = $(event.target).data(‘color-hex’)
this.render()
}
}, // ...
})
你始终可以创建一些没有端点的其他模型和集合。这些可以帮助你存储不必一定要保留在后端的数据, 或者可以是临时的数据。将它们存储在模型中可以使你受益于能够监听更改。相关视图甚至多个视图可以观察这些模型, 并在必要时重新渲染它们。
想象一下, 如果你实际上将状态跟踪变量存储在视图上, 并且每次更改它们都必须调用render方法。就用户在屏幕上的体验而言, 仅缺少一次对此渲染方法的调用可能会使你的应用程序处于损坏状态。此外, 对于小视图, 你可能必须在多个视图对象上同步这些状态变量, 然后还必须在它们上调用render方法。
错误8:使用jQuery” .on()”代替委托事件
我认为Backbone.js具有一种处理DOM事件的出色方法。不使用它会带来很多缺点。 jQuery的” .on()”事件绑定函数听起来很方便, 但从长远来看, 通常会很麻烦。例如, 当元素与DOM分离时, jQuery使用” .on()”自动删除绑定到元素的所有事件处理程序。这意味着, 如果你将根元素与DOM分离并重新附加, 则试图从视图内绑定到的任何DOM事件都需要重新绑定。
var AudioPlayerControls = Backbone.View.extend({
events: {
‘click .btn-play, .btn-pause’: function() { /* ... */ }, ‘click .btn-prev’: function() { /* ... */ }, ‘click .btn-next’: function() { /* ... */ }, ‘click .btn-shuffle’: function() { /* ... */ }, ‘click .btn-repeat’: function() { /* ... */ }
}, // ...
})
当与该视图相对应的元素重新附加到DOM时, 你要做的就是在视图上调用” delegateEvents()”以绑定所有这些事件。
请注意, 重要的是要了解如何绑定这些事件。 Backbone.js实际上不是将事件绑定到选择器指定的元素上, 而是将事件处理程序绑定到视图的根元素。在几乎所有情况下, 此方法都可以正常工作, 实际上, 对于我们的大多数需求而言, 效果更好。更改或替换视图DOM子树中的子元素不需要Backbone.js在新元素上再次绑定每个事件。现有的听众会继续工作。
但是, 这会阻止某些事件被监听。一个示例是你可能希望在”窗口”或子可滚动元素上侦听滚动事件。如果是子元素, 则可以为该元素创建一个子视图, 并在那里处理事件。
总结
Backbone.js是一个非常紧凑但可扩展的框架, 对于需要大量灵活性的Web应用程序来说, 它是绝佳的选择。与Angular.js和Ember.js之类的框架总是可以告诉你如何做自己的事情不同, Backbone.js可以退一步, 为你提供强大的工具集, 并让你决定如何使用他们。我希望这个针对初学者的Backbone.js教程将帮助你避免一些常见的开发错误, 并以此为基础构建一些惊人的东西。