隨著JavaScript程序變得越來越復(fù)雜,往往需要一個團(tuán)隊協(xié)作開發(fā),這時代碼的模塊化和組織規(guī)范就變得異常重要了。MVC模式就是代碼組織的經(jīng)典模式。
(……MVC介紹。)
(1)Model
Model表示數(shù)據(jù)層,也就是程序需要的數(shù)據(jù)源,通常使用JSON格式表示。
(2)View
View表示表現(xiàn)層,也就是用戶界面,對于網(wǎng)頁來說,就是用戶看到的網(wǎng)頁HTML代碼。
(3)Controller
Controller表示控制層,用來對原始數(shù)據(jù)(Model)進(jìn)行加工,傳送到View。
由于網(wǎng)頁編程不同于客戶端編程,在MVC的基礎(chǔ)上,JavaScript社區(qū)產(chǎn)生了各種變體框架MVP(Model-View-Presenter)、MVVM(Model-View-ViewModel)等等,有人就把所有這一類框架的各種模式統(tǒng)稱為MV*。
框架的優(yōu)點在于合理組織代碼、便于團(tuán)隊合作和未來的維護(hù),缺點在于有一定的學(xué)習(xí)成本,且限制你只能采取它的寫法。
MVC框架(尤其是大型框架)有一個嚴(yán)重的缺點,就是會產(chǎn)生用戶的重度依賴。一旦框架本身出現(xiàn)問題或者停止更新,用戶的處境就會很困難,維護(hù)和更新成本極高。
ES6的到來,使得JavaScript語言有了原生的模塊解決方案。于是,開發(fā)者有了另一種選擇,就是不使用MVC框架,只使用各種單一用途的模塊庫,組合完成一個項目。下面是可供選擇的各種用途的模塊列表。
輔助功能庫(Helper Libraries)
路由庫(Routing)
Promise庫
客戶端與服務(wù)器的通信庫
動畫庫(Animation)
輔助開發(fā)庫(Development Assistance)
流程控制和架構(gòu)(Flow Control/Architecture)
數(shù)據(jù)綁定(Data-binding)
模板庫(Templating)
微框架(Micro-Framework)
某些情況下,可以使用微型框架,作為項目開發(fā)的起點。
<script src="https://atts.w3cschool.cn/attachments/image/cimg/jquery.js"></script>
<script src="https://atts.w3cschool.cn/attachments/image/cimg/underscore.js"></script>
<script src="https://atts.w3cschool.cn/attachments/image/cimg/backbone.js"></script>
<script src="https://atts.w3cschool.cn/attachments/image/cimg/jst.js"></script>
<script src="https://atts.w3cschool.cn/attachments/image/cimg/router.js"></script>
<script src="https://atts.w3cschool.cn/attachments/image/cimg/init.js"></script>
Backbone是最早的JavaScript MVC框架,也是最簡化的一個框架。它的設(shè)計思想是,只提供最基本的功能,給用戶提供最大的自由。這意味著,好的一面是它沒有一整套規(guī)則,強(qiáng)制你接受,壞的一面是很多功能你必須自己實現(xiàn)。Backbone的體積相當(dāng)小,最小化后只有30多KB。
定義一個對象,表示W(wǎng)eb應(yīng)用。
var AppName = {
Models :{},
Views :{},
Collections :{},
Controllers :{}
};
上面代碼表示,應(yīng)用由四部分組成:Model、Collection、Controller和View。
定義Model,表示數(shù)據(jù)的一個基本單位。
AppName.Models.Person = Backbone.Model.extend({
urlRoot: "/persons"
});
定義Collection,表示Model的集合。
AppName.Collections.Library = Backbone.Collection.extend({
model: AppName.Models.Book
});
上面代碼表示,Collection對象必須有model屬性,指明由哪一個model構(gòu)成。
定義一個View。
AppName.Views.Modals.AcceptDecline = Backbone.View.Extend({
el: ".modal-accept",
events: {
"ajax:success .link-accept" :"acceptSuccess",
"ajax:error .link-accept" :"acceptError"
},
acceptSuccess :function(evt, response) {
this.$el.modal("hide");
alert('Cool! Thanks');
},
acceptError :function(evt, response) {
var $modalContent = this.$el.find('.panel-modal');
$modalContent.append("Something was wrong!");
}
});
View對象必須有el屬性,指明當(dāng)前View綁定的DOM節(jié)點,events屬性指明事件和對應(yīng)的方法。
定義一個Controller。
AppName.Controllers.Person = {};
AppName.Controllers.Person.show = function(id) {
var aMa = new AppName.Models.Person({id: id});
aMa.updateAge(25);
aMa.fetch().done(function(){
var view = new AppName.Views.Show({model: aMa});
});
};
最后,定義路由,啟動應(yīng)用程序。
var Workspace = Backbone.Router.extend({
routes: {
"*" :"wholeApp",
"users/:id" :"usersShow",
"users/:id/orders/" :"ordersIndex"
},
wholeApp :AppName.Controller.Application.default,
usersShow :AppName.Controller.Users.show,
ordersIndex :AppName.Controller.Orders.index
});
new Workspace();
Backbone.history.start({pushState: true});
Backbone.View方法用于定義視圖類。
var AppView = Backbone.View.extend({
render: function(){
$('main').append('<h1>一級標(biāo)題</h1>');
}
});
上面代碼通過Backbone.View的extend方法,定義了一個視圖類AppView。該類內(nèi)部有一個render方法,用于將視圖放置在網(wǎng)頁上。
使用的時候,需要先新建視圖類的實例,然后通過實例,調(diào)用render方法,從而讓視圖在網(wǎng)頁上顯示。
var appView = new AppView();
appView.render();
上面代碼新建視圖類AppView的實例appView,然后調(diào)用appView.render,網(wǎng)頁上就會顯示指定的內(nèi)容。
新建視圖實例時,通常需要指定Model。
var document = new Document({
model: doc
});
視圖還可以定義initialize方法,生成實例的時候,會自動調(diào)用該方法對實例初始化。
var AppView = Backbone.View.extend({
initialize: function(){
this.render();
},
render: function(){
$('main').append('<h1>一級標(biāo)題</h1>');
}
});
var appView = new AppView();
上面代碼定義了initialize方法之后,就省去了生成實例后,手動調(diào)用appView.render()的步驟。
除了直接在render方法中,指定“視圖”所綁定的網(wǎng)頁元素,還可以用視圖的el屬性指定網(wǎng)頁元素。
var AppView = Backbone.View.extend({
el: $('main'),
render: function(){
this.$el.append('<h1>一級標(biāo)題</h1>');
}
});
上面的代碼與render方法直接綁定網(wǎng)頁元素,效果完全一樣。上面代碼中,除了el屬性,還是$el屬性,前者代表指定的DOM元素,后者則表示該DOM元素對應(yīng)的jQuery對象。
如果不指定el屬性,也可以通過tagName屬性和className屬性指定。
var Document = Backbone.View.extend({
tagName: "li",
className: "document",
render: function() {
// ...
}
});
視圖的template屬性用來指定網(wǎng)頁模板。
var AppView = Backbone.View.extend({
template: _.template("<h3>Hello <%= who %><h3>"),
});
上面代碼中,underscore函數(shù)庫的template函數(shù),接受一個模板字符串作為參數(shù),返回對應(yīng)的模板函數(shù)。有了這個模板函數(shù),只要提供具體的值,就能生成網(wǎng)頁代碼。
var AppView = Backbone.View.extend({
el: $('#container'),
template: _.template("<h3>Hello <%= who %><h3>"),
initialize: function(){
this.render();
},
render: function(){
this.$el.html(this.template({who: 'world!'}));
}
});
上面代碼的render就調(diào)用了template方法,從而生成具體的網(wǎng)頁代碼。
實際應(yīng)用中,一般將模板放在script標(biāo)簽中,為了防止瀏覽器按照J(rèn)avaScript代碼解析,type屬性設(shè)為text/template。
<script type="text/template" data-name="templateName">
<!-- template contents goes here -->
</script>
可以使用下面的代碼編譯模板。
window.templates = {};
var $sources = $('script[type="text/template"]');
$sources.each(function(index, el) {
var $el = $(el);
templates[$el.data('name')] = _.template($el.html());
});
events屬性用于指定視圖的事件及其對應(yīng)的處理函數(shù)。
var Document = Backbone.View.extend({
events: {
"click .icon": "open",
"click .button.edit": "openEditDialog",
"click .button.delete": "destroy"
}
});
上面代碼中一個指定了三個CSS選擇器的單擊事件,及其對應(yīng)的三個處理函數(shù)。
listento方法用于為特定事件指定回調(diào)函數(shù)。
var Document = Backbone.View.extend({
initialize: function() {
this.listenTo(this.model, "change", this.render);
}
});
上面代碼為model的change事件,指定了回調(diào)函數(shù)為render。
remove方法用于移除一個視圖。
updateView: function() {
view.remove();
view.render();
};
在父視圖中可以調(diào)用子視圖。下面就是一種寫法。
render : function (){
this.$el.html(this.template());
this.child = new Child();
this.child.appendTo($.('.container-placeholder').render();
}
Router是Backbone提供的路由對象,用來將用戶請求的網(wǎng)址與后端的處理函數(shù)一一對應(yīng)。
首先,新定義一個Router類。
Router = Backbone.Router.extend({
routes: {
}
});
Backbone.Router對象中,最重要的就是routes屬性。它用來設(shè)置路徑的處理方法。
routes屬性是一個對象,它的每個成員就代表一個路徑處理規(guī)則,鍵名為路徑規(guī)則,鍵值為處理方法。
如果鍵名為空字符串,就代表根路徑。
routes: {
'': 'phonesIndex',
},
phonesIndex: function () {
new PhonesIndexView({ el: 'section#main' });
}
星號代表任意路徑,可以設(shè)置路徑參數(shù),捕獲具體的路徑值。
var AppRouter = Backbone.Router.extend({
routes: {
"*actions": "defaultRoute"
}
});
var app_router = new AppRouter;
app_router.on('route:defaultRoute', function(actions) {
console.log(actions);
})
上面代碼中,根路徑后面的參數(shù),都會被捕獲,傳入回調(diào)函數(shù)。
路徑規(guī)則的寫法。
var myrouter = Backbone.Router.extend({
routes: {
"help": "help",
"search/:query": "search"
},
help: function() {
...
},
search: function(query) {
...
}
});
routes: {
"help/:page": "help",
"download/*path": "download",
"folder/:name": "openFolder",
"folder/:name-:mode": "openFolder"
}
router.on("route:help", function(page) {
...
});
設(shè)置了router以后,就可以啟動應(yīng)用程序。Backbone.history對象用來監(jiān)控url的變化。
App = new Router();
$(document).ready(function () {
Backbone.history.start({ pushState: true });
});
打開pushState方法。如果應(yīng)用程序不在根目錄,就需要指定根目錄。
Backbone.history.start({pushState: true, root: "/public/search/"})
Model代表單個的對象實體。
var User = Backbone.Model.extend({
defaults: {
name: '',
email: ''
}
});
var user = new User();
上面代碼使用extend方法,生成了一個User類,它代表model的模板。然后,使用new命令,生成一個Model的實例。defaults屬性用來設(shè)置默認(rèn)屬性,上面代碼表示user對象默認(rèn)有name和email兩個屬性,它們的值都等于空字符串。
生成實例時,可以提供各個屬性的具體值。
var user = new User ({
id: 1,
name: 'name',
email: 'name@email.com'
});
上面代碼在生成實例時,提供了各個屬性的具體值。
Model實例必須有一個屬性,作為區(qū)分其他實例的主鍵。這個屬性的名稱,由idAttribute屬性設(shè)定,一般是設(shè)為id。
var Music = Backbone.Model.extend({
idAttribute: 'id'
});
get方法用于返回Model實例的某個屬性的值。
var user = new User({ name: "name", age: 24});
var age = user.get("age"); // 24
var name = user.get("name"); // "name"
set方法用于設(shè)置Model實例的某個屬性的值。
var User = Backbone.Model.extend({
buy: function(newCarsName){
this.set({car: newCarsName });
}
});
var user = new User({name: 'BMW',model:'i8',type:'car'});
user.buy('Porsche');
var car = user.get("car"); // ‘Porsche’
on方法用于監(jiān)聽對象的變化。
var user = new User({name: 'BMW',model:'i8'});
user.on("change:name", function(model){
var name = model.get("name"); // "Porsche"
console.log("Changed my car’s name to " + name);
});
user.set({name: 'Porsche'});
// Changed my car’s name to Porsche
上面代碼中的on方法用于監(jiān)聽事件,“change:name”表示name屬性發(fā)生變化。
該屬性用于指定服務(wù)器端對model進(jìn)行操作的路徑。
var User = Backbone.Model.extend({
urlRoot: '/user'
});
上面代碼指定,服務(wù)器對應(yīng)該Model的路徑為/user。
fetch事件用于從服務(wù)器取出Model。
var user = new User ({id: 1});
user.fetch({
success: function (user){
console.log(user.toJSON());
}
})
上面代碼中,user實例含有id屬性(值為1),fetch方法使用HTTP動詞GET,向網(wǎng)址“/user/1”發(fā)出請求,從服務(wù)器取出該實例。
save方法用于通知服務(wù)器新建或更新Model。
如果一個Model實例不含有id屬性,則save方法將使用POST方法新建該實例。
var User = Backbone.Model.extend({
urlRoot: '/user'
});
var user = new User ();
var userDetails = {
name: 'name',
email: 'name@email.com'
};
user.save(userDetails, {
success: function (user) {
console.log(user.toJSON());
}
})
上面代碼先在類中指定Model對應(yīng)的網(wǎng)址是/user,然后新建一個實例,最后調(diào)用save方法。它有兩個參數(shù),第一個是實例對象的具體屬性,第二個參數(shù)是一個回調(diào)函數(shù)對象,設(shè)定success事件(保存成功)的回調(diào)函數(shù)。具體來說,save方法會向/user發(fā)出一個POST請求,并將{name: 'name', email: 'name@email.com'}作為數(shù)據(jù)提供。
如果一個Model實例含有id屬性,則save方法將使用PUT方法更新該實例。
var user = new User ({
id: 1,
name: '張三',
email: 'name@email.com'
});
user.save({name: '李四'}, {
success: function (model) {
console.log(user.toJSON());
}
});
上面代碼中,對象實例含有id屬性(值為1),save將使用PUT方法向網(wǎng)址“/user/1”發(fā)出請求,從而更新該實例。
destroy方法用于在服務(wù)器上刪除該實例。
var user = new User ({
id: 1,
name: 'name',
email: 'name@email.com'
});
user.destroy({
success: function () {
console.log('Destroyed');
}
});
上面代碼的destroy方法,將使用HTTP動詞DELETE,向網(wǎng)址“/user/1”發(fā)出請求,刪除對應(yīng)的Model實例。
Collection是同一類Model的集合,比如Model是動物,Collection就是動物園;Model是單個的人,Collection就是一家公司。
var Song = Backbone.Model.extend({});
var Album = Backbone.Collection.extend({
model: Song
});
上面代碼中,Song是Model,Album是Collection,而且Album有一個model屬性等于Song,因此表明Album是Song的集合。
Model的實例可以直接放入Collection的實例,也可以用add方法添加。
var song1 = new Song({ id: 1 ,name: "歌名1", artist: "張三" });
var song2 = new Music ({id: 2,name: "歌名2", artist: "李四" });
var myAlbum = new Album([song1, song2]);
var song3 = new Music({ id: 3, name: "歌名3",artist:"趙五" });
myAlbum.add(song3);
remove方法用于從Collection實例中移除一個Model實例。
myAlbum.remove(1);
上面代碼表明,remove方法的參數(shù)是model實例的id屬性。
get方法用于從Collection中獲取指定id的Model實例。
myAlbum.get(2))
fetch方法用于從服務(wù)器取出Collection數(shù)據(jù)。
var songs = new Backbone.Collection;
songs.url = '/songs';
songs.fetch();
var obj = {};
_.extend(obj, Backbone.Events);
obj.on("show-message", function(msg) {
$('#display').text(msg);
});
obj.trigger("show-message", "Hello World");
更多建議: