對(duì)JavaScript客戶端應(yīng)用編程的一些建議
你可能注意到了,最近的一段時(shí)間越來(lái)越多的Web應(yīng)用有變復(fù)雜的趨勢(shì),重心從服務(wù)端慢慢向著客戶端轉(zhuǎn)移。 這是個(gè)正常的趨勢(shì)么?我不知道。支持和反對(duì)者的討論就像是在討論復(fù)活者和圣誕節(jié)哪一個(gè)更好一樣; 很難說(shuō)哪一方觀點(diǎn)就是完全正確的。因此,本文不會(huì)探討究竟哪一方是對(duì)的,不過(guò)我還是試圖解釋一下使用大家所熟知的面向?qū)ο缶幊桃苍S可以成功的解決客戶端編程中存在的一些問(wèn)題。
不太規(guī)范的代碼的示例
為了顧及一個(gè)應(yīng)用的響應(yīng)以及用戶體驗(yàn), 導(dǎo)致我們創(chuàng)建了持續(xù)增長(zhǎng)的復(fù)雜的代碼, 這些代碼變得難于理解和維護(hù)。 你可以輕松的想到在沒(méi)有任何構(gòu)架和遵循規(guī)則構(gòu)建出客戶端的JavaScript應(yīng)用代碼將會(huì)這樣:
$(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(''); } }); }); });
維護(hù)這一類的代碼將會(huì)很難。因?yàn)檫@短短的一段代碼與很多地方都有關(guān)聯(lián): 它控制著很多的事件 (站點(diǎn), 用戶, 網(wǎng)絡(luò)事件), 它要處理用戶的操作事件, 要解析服務(wù)器返回的應(yīng)答并且產(chǎn)生HTML代碼。 有人可能說(shuō): “是的,你說(shuō)的對(duì), 但是如果這不是一個(gè)客戶端單頁(yè)的頁(yè)面應(yīng)用?這最多算是一次過(guò)度使用jQuery類庫(kù)的例子” ——不是很有說(shuō)服力的觀點(diǎn), 因?yàn)楸娝苤?,易于維護(hù)和精心設(shè)計(jì)的代碼是非常重要的。特別是許多的工具或者是框架致力于保持代碼可用以便于我們能更簡(jiǎn)單的去測(cè)試、維護(hù)、重用、和擴(kuò)展它。
MVC是什么?
談到這里。我們能受益于那些基于MVC的JavaScript框架,但這些框架大部分不使用MVC,并且相當(dāng)于Model和Videw的一種結(jié)合,或者在二都之間的一些東西,這很難去分清。這就是為什么說(shuō)大部分的Javascript框架是基于MV*。
改變方法或許可以提供項(xiàng)目中客戶端的組織和架構(gòu),這使得代碼可以在很長(zhǎng)的一段時(shí)間內(nèi)容易維護(hù),即使重構(gòu)已經(jīng)有的代碼也變得相對(duì)容易。知道他如何工作和下面一些問(wèn)題的答案是必需要要記住的。
- 我的應(yīng)用里有哪些類型的數(shù)據(jù)?-Model
- 用戶應(yīng)該看到什么?-View
- 誰(shuí)是和用戶交互的程序?-Controller
使用MVC框架重構(gòu)代碼
受用MVC重構(gòu)代碼有什么好處?
- 解除DOM和Ajax的依賴
- 代碼有更好的結(jié)構(gòu),并且更容易測(cè)試。
- 從 $(document).ready()中刪除多余的代碼,只留下使用Model創(chuàng)建Links的部分。
讓我們使用一些簡(jiǎn)單步驟來(lái)重構(gòu)一個(gè)典型的代碼塊
步驟 1: 創(chuàng)建視圖并移動(dòng)Ajax請(qǐng)求
我們開始解除DOM和Ajax的依賴. 使用prototypes建造者,模式創(chuàng)建'Animals' 對(duì)象,并且添加一個(gè) 'add' 方法.同時(shí)創(chuàng)建視圖 '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: 使用事件解除依賴.
這個(gè)例子,利用MVC框架是關(guān)鍵。我們將會(huì)用到事件機(jī)制, 事件使我們結(jié)合和觸發(fā)自定義事件. 因此,我們創(chuàng)建新的“AnimalsView”和“NewAnimalView”,并且賦予它們不同的顯示animals的職責(zé)。 使用事件就來(lái)區(qū)別職責(zé)非常簡(jiǎn)單。如果在方法和事件之間傳遞職責(zé),如下所示:
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: 傳遞數(shù)據(jù)結(jié)構(gòu)到核心框架
最后,最重要的一步,我們使用: 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 }); });
總結(jié)
我們已經(jīng)實(shí)現(xiàn)什么呢?我們?cè)诟叨鹊某橄笊瞎ぷ鳌4a的維護(hù)、重構(gòu)和擴(kuò)展變得更容易。我們極大的優(yōu)化了代碼結(jié)果,是不是很迷人?太棒了。但是,我可能要給你潑冷水,即使最好的框架,開發(fā)的代碼仍舊是脆弱并且難以維護(hù)。因此,如果你認(rèn)為使用了一個(gè)較好的MV*框架能解決所有代碼上的問(wèn)題是錯(cuò)誤的。記住在重構(gòu)過(guò)程中,經(jīng)歷了第二步,代碼會(huì)變得好很多,我們不使用框架的主要組件。
記住MV*框架是好的這一點(diǎn),但是所有關(guān)注在‘How'去開發(fā)一個(gè)應(yīng)用,這讓程序開發(fā)人員頭決定‘What'。每個(gè)框架的一個(gè)補(bǔ)充,尤其是當(dāng)項(xiàng)目的Domain很復(fù)雜,將是Domain驅(qū)動(dòng)設(shè)計(jì)方法,這將更關(guān)注與下面的方面:“what”, 把需求轉(zhuǎn)化為真正的產(chǎn)品的一個(gè)過(guò)程。但是,這是我們要討論的另外一個(gè)主題。
相關(guān)文章
學(xué)習(xí)js在線html(富文本,所見即所得)編輯器
需要一個(gè)可以編輯同時(shí)又可顯效果的編輯框。textarea不行,它只能用來(lái)輸入純文本,不能顯示顏色、斜體之類的文字樣式,就像記事本,本文介紹所見即所得編輯器實(shí)現(xiàn)原理2012-12-12簡(jiǎn)介JavaScript中search()方法的使用
這篇文章主要介紹了簡(jiǎn)介JavaScript中search()方法的使用,是JS入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-06-06javascript學(xué)習(xí)筆記(四) Number 數(shù)字類型
數(shù)字格式化方法toFixed()、toExponential()、toPrecision(),三個(gè)方法都四舍五入2012-06-06