Backbone.js 開發人員最常犯的 8 個錯誤

已發表: 2022-03-11

Backbone.js 是一個簡約框架,旨在提供一組簡單的數據結構和功能,您可以使用它們來創建結構化 Web 應用程序的前端。 開箱即用,Backbone.js 的組件提供了一個直觀的環境,當您在後端處理模型和視圖時,您可能已經熟悉該環境。 Backbone.js 中的模型和集合很簡單,但它們具有一些非常有用的特性,例如可以輕鬆地將它們與 REST JSON API 集成的選項。 但它們也足夠靈活,幾乎可以適應任何實際用途。

在這個 Backbone.js 教程中,我們將看看自由開發者在第一次嘗試學習 Backbone.js 時經常犯的一些常見錯誤以及避免這些錯誤的方法。

錯誤 #1:忽略 Backbone.js 的功能庫

Backbone.js 可能是一個極簡框架,但它(連同 Underscore.js)提供了大量特性和功能,可以輕鬆滿足開發現代 Web 應用程序時出現的最基本和一些關鍵需求。 初學者開發人員經常犯的一個常見錯誤是,他們將 Backbone.js 視為另一種類似 MVC 的 Web 客戶端框架。 儘管本節討論了一些非常明顯的事情,但對於 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 做這樣的事情是錯誤的。

錯誤 #3:低估渲染成本

由於 Backbone.js 使得隨意渲染和重新渲染 DOM 或響應事件變得非常容易,我們經常忽略這對 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 方法將被連續調用多次,服務器響應中的每個模型一次。 足夠大的模型足以使您的應用程序卡頓並破壞用戶體驗。 有時一個小的響應就足夠了,這取決於正在渲染的視圖的複雜性。

一個非常快速的解決方案是簡單地不為正在添加的每個模型調用渲染方法。 在這種情況下,模型將被批量添加,您實際上可以做一些事情來使渲染方法僅在被調用但在指定時間內不被重新調用時觸發。 Backbone.js 的依賴 Underscore.js 帶有一個方便的實用函數:“_.debounce”。 您需要利用這一點的所有內容是使用以下代碼更改事件綁定 JavaScript 行:

 this.listenTo(this.collection, 'add', _.debounce(_.bind(this.render), 128))

這將導致每次“添加”事件發生時都會觸發事件回調,但是,它將在實際調用渲染方法之前從最後一個事件等待 128 毫秒。

在大多數情況下,這將被視為類似快速修復的解決方案。 事實上,有更合適的方法來避免渲染抖動。 Trello 背後的開發人員曾經寫過一篇博客文章,討論了他們在使用 Backbone.js 時提高渲染性能的經驗和方法。

錯誤 #4:讓事件偵聽器超出其使用範圍

無論您使用什麼 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 中處理前端與後端服務交互的組件是“Sync”。 該組件公開了許多屬性,您可以輕鬆覆蓋這些屬性以自定義 Backbone.js 與 API 端點交互的方式。 實際上,也可以將默認的同步機制替換為至少可以說不那麼傳統的東西,例如使用 localStorage 來持久化數據,而不是後端服務。

存在許多插件,可以輕鬆自定義 Backbone.js 的同步行為。 例如,名為 Backbone.dualStorage 的插件允許您同時使用後端服務和 localStorage 來持久化數據。 當您的應用程序離線時,該插件使用 localStorage 來保持為來自緩存數據的請求提供服務,並跟踪您稍後在線時可能與服務器同步的更改。

儘管將 Backbone.js 與設計為 RESTful 並與之兼容的後端一起使用更容易使用,但這並不意味著 Backbone.js 可以使用所有這些。 通過對默認 Backbone.js 同步機制進行一些更改,您可以使其適應各種後端服務 API 和編碼格式。

值得一提的是,Backbone.js 的其他部分也很靈活,並且在某些方面是可選的。 例如,您不必使用 Underscore.js 附帶的默認模板引擎。 您甚至不必使用 Backbone.js 的視圖組件,並且可以根據需要將其完全替換為其他東西。

錯誤 #7:將數據存儲在視圖而不是模型中

作為初學者學習 Backbone.js,我們可能經常犯的一個錯誤是將數據作為屬性直接存儲在視圖上。 該數據可能用於跟踪某些狀態或某些用戶選擇。 這是應該避免的。

 var AudioPlayerVisualizer = Backbone.View.extend({ events: { 'click .btn-color': function(event) { this.colorHex = $(event.target).data('color-hex') this.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 教程將幫助您避免一些常見的開發錯誤並用它構建一些令人驚嘆的東西。