你可能注意到了,最近的一段時間越來越多的Web應用有變得複雜的趨勢,重心從服務端慢慢轉移到客戶端。 這是正常的趨勢麼?我不知道。支持和反對者的討論就像是在討論復活者和聖誕節哪一個更好一樣; 很難說哪一方觀點就是完全正確的。因此,本文不會探討究竟哪一方是對的,不過我還是試圖解釋一下使用大家所熟知的物件導向程式設計也許可以成功的解決客戶端程式設計中存在的一些問題。
不太規範的程式碼的範例
為了顧及一個應用程式的回應以及使用者體驗, 導致我們創建了持續增長的複雜的程式碼, 這些程式碼變得難於理解和維護。 你可以輕鬆的想到在沒有任何架構和遵循規則建立出客戶端的JavaScript應用程式碼將會這樣:
$(function(){ $('#form').submit(function(e) { e.preventDefault(); $.ajax({ url: '/animals', type: 'POST', dataType: 'json', data: { text: $('#new-animal').find('textarea').val() }, success: function(data) { $('#animals').append('<li>' + data.text + '</li>'); $('#new-animal').find('textarea').val(''); } }); }); });
維護這一類的程式碼將會很難。因為這短短的一段程式碼與很多地方都有關聯: 它控制著很多的事件 (站點, 用戶, 網路事件), 它要處理用戶的操作事件, 要解析伺服器返回的應答並且產生HTML代碼。 有人可能說: “是的,你說的對, 但是如果這不是一個客戶端單頁的頁面應用?這最多算是一次過度使用jQuery類庫的例子” ——不是很有說服力的觀點, 因為眾所周知,易於維護和精心設計的程式碼是非常重要的。特別是許多的工具或是框架致力於保持程式碼可用以便於我們能更簡單的去測試、維護、重複使用、和擴展它。
MVC是什麼?
談到這裡。我們能受益於那些基於MVC的JavaScript框架,但這些框架大部分不使用MVC,並且相當於Model和Videw的一種結合,或者在二都之間的一些東西,這很難去分清。這就是為什麼說大部分的Javascript框架是基於MV*。
改變方法或許可以提供專案中客戶端的組織和架構,這使得程式碼可以在很長的一段時間內容易維護,即使重構已經有的程式碼也變得相對容易。知道他如何工作和下面一些問題的答案是必需要記住的。
使用MVC框架重建程式碼
受用MVC重構程式碼有什麼好處?
讓我們使用一些簡單步驟來重構一個典型的程式碼區塊
步驟 1: 建立視圖並移動Ajax請求
我們開始解除DOM和Ajax的依賴. 使用prototypes建造者,模式創建'Animals' 對象,並且添加一個'add' 方法.同時創建視圖'NewAnimalView' , 並且添加方法'addAnimal'、 'appendAnimal' 、 'clearInput'.
程式碼如下:
var Animals = function() { }; Animals.prototype.add = function (options) { $.ajax({ url: '/animals', type: 'POST', dataType: 'json', data: { text: options.text }, success: options.success }); }; var NewAnimalView = function (options) { this.animals = options.animals; var add = $.proxy(this.addAnimal, this); $('# form').submit(add); }; NewAnimalView.prototype.addAnimal = function(e) { e.preventDefault(); var self = this; this.animals.add({ text: $('#new-animal textarea').val(), success: function(data) { self.appendAnimal (data.text); self.clearInput(); } }); }; NewAnimalView.prototype.appendAnimal = function(text) { $('#animals ul').append('<li>' + data.text + '</li>'); }; NewAnimalView.prototype.clearInput = function() { $('#new-animal textarea').val(''); }; $(document).ready(function() { var animals = new Animals(); new NewAnimalView({ animals: animals }); });
步驟 2: 使用事件解除依賴.
這個例子,利用MVC架構是關鍵。我們將會用到事件機制, 事件使我們結合和觸發自訂事件. 因此,我們創建新的“AnimalsView”和“NewAnimalView”,並且賦予它們不同的顯示animals的職責。 使用事件就來區別職責非常簡單。如果在方法和事件之間傳遞職責,如下所示:
var events = _.clone(Backbone.Events); var Animals = function() { }; Animals.prototype.add = function(text) { $.ajax({ url: '/animals', type: 'POST', dataType: 'json', data: { text: text }, success: function(data) { events.trigger('animal:add', data.text); } }); }; var NewAnimalView = function(options) { this.animals = options.animals; events.on('animal:add', this.clearAnimal, this); var add = $.proxy(this.addAnimal, this); $('# form').submit(add); }; NewAnimalView.prototype.addAnimal = function(e) { e.preventDefault(); this.animals.add($('#new-animal textarea').val()); }; NewAnimalView.prototype.clearInput = function() { $('#new-animal textarea').val(''); }; var AnimalsView = function() { events.on('animal:add', this.appendAnimal, this); }; AnimalsView.prototype.appendAnimal = function(text) { $('#animals ul').append('<li>' + data.text + '</li>'); }; $(document).ready(function() { var animals = new Animals(); new NewAnimalView({ animals: animals }); new AnimalsView(); });
步驟 3: 傳遞資料結構到核心架構
最後,最重要的一步,我們使用: models, views and collections.
var Animal = Backbone.Model.extend({ url: '/animals' }); var Animals = Backbone.Collection.extend({ model: Animal }); var AnimalsView = Backbone.View.extend({ initialize: function() { this.collection.on('add', this.appendAnimal, this); }, appendAnimal: function(animal) { this.$('ul').append('<li>' + animal.escape('text') + '</li>'); } }); var NewAnimalView = Backbone.View.extend({ events: { 'submit form': 'addAnimal' }, initialize: function() { this.collection.on('add', this.clearInput, this); }, addAnimal: function(e) { e.preventDefault(); this.collection.create({ text: this.$('textarea').val() }); }, clearInput: function() { this.$('textarea').val(''); } }); $(document).ready(function() { var animals = new Animals(); new NewAnimalView({ el: $('#new-animal'), collection: animals }); new AnimalsView({ el: $('#animals'), collection: animals }); });
總結
我們已經實現什麼呢?我們在高度的抽像上工作。程式碼的維護、重構和擴展變得更容易。我們極大的優化了程式碼結果,是不是很迷人?太棒了。但是,我可能要給你潑冷水,即使最好的框架,開發的程式碼仍舊是脆弱且難以維護。因此,如果你認為使用了一個較好的MV*框架能解決所有程式碼上的問題是錯誤的。記住在重構過程中,經歷了第二步,程式碼會變得好很多,我們不使用框架的主要元件。
記住MV*框架是好的這一點,但是所有關注在‘How'去開發一個應用,這讓程序開發人員頭決定‘What'。每個框架的一個補充,尤其是當專案的Domain很複雜,將是Domain驅動設計方法,這將更關注與下面的方面:“what”, 把需求轉化為真正的產品的一個過程。但是,這是我們要討論的另一個主題。