樂帝當年學習backbone時。最開始是看官網todoMVC的實作。後來了解到requireJS便于管理JS代碼。就對官網代碼做了requireJS管理。但此時樂帝感覺此時的todoMVC仍然不夠簡明,為了加深對MVC架構的了解。樂帝對原有appview代碼進行了重構,将相關顯示子產品單獨提取出自稱view。實作view原子化。樂帝已經将這個項目上傳(下載下傳位址)。
增加requireJS的檔案夾結構:
這裡主要用到templates用于放置view相應的模闆。views則相應backbone中view檔案。假設說backbone是前端MVC,那麼model是對資料建立模型。collection則是對model統一管理,view則起到控制器的作用,用于填充資料到模闆,并渲染模闆到顯示。model、collection起到M作用。view起到C的作用,模闆則起到V的作用。
然後我們看一下todoMVC的效果圖:
從終于效果圖。我們能夠分析出,要對原有appview中解耦出原子view。就須要推斷出哪些是原子view。原子view須要具備兩點:
- 具有動态互動效果
- 與其它頁面部分獨立
當然這裡的原子view定義還值得商榷。樂帝依據以上兩個原則對view進行了又一次劃分。 且看views檔案夾結構:
相應模闆檔案夾結構:
須要注意的是,這裡appview并沒有相應的模闆。而是通過設定el: "#todoapp",在index.html檔案裡,統一對原子view進行管理。
以下以ToggleAllView類源碼為樣例,我們分析下,原子view職能的組成:
define([
'jquery',
'underscore',
'backbone',
'text!templates/toggleAll.html'
], function($, _, Backbone, toggleTemplate) {
var ToggleAllView = Backbone.View.extend({
toggleTemplate: _.template(toggleTemplate),
events: {
"click #toggle-all": "toggleAllComplete"
},
initialize: function() {
this.listenTo(this.collection, "all", this.render); //除了todoview與todomodel一一相應
// 其它相關操作都會監聽collection
},
render: function() {
this.$el.html(this.toggleTemplate());
var done = this.collection.done().length;
var remaining = this.collection.remaining().length;
this.allCheckbox = this.$("#toggle-all")[0];
this.allCheckbox.checked = !remaining;
return this;
},
toggleAllComplete: function() {
var done = this.allCheckbox.checked;
this.collection.each(function(todo) {
todo.save({
done: done
});
}); //這裡通過推斷單選框是否選中。改動全部modeldone屬性
}
});
return ToggleAllView;
});
上述代碼中職能主要有例如以下幾種:
- 設定el或tagname,用于定義在上一層view放置的位置,或包裹的标簽
- 設定相應模闆(Template)
- 定義互動事件,并連帶定義互動函數
- 初始化函數(initialize),一般設定對collection或者model的監聽,用于view之間的通信
- 渲染函數(render),用于渲染資料到模闆中。設定其它一些全局函數
由此可見。原子view将職能劃分的非常清楚。這也是前端MVC架構的原因,而不是之前純腳本時代,代碼間高度耦合。牽一發而動全身。 對于學習backbone,原子view和appview各自代碼都不難了解。難于了解或者它精妙之處,在于對事件的監聽機制,正是這樣的機制,處理了view之間的通信,進而将松散的view拼裝成性能優良的整理。
todoView的監聽:
initialize: function() {
this.listenTo(this.model, "change", this.render);
this.listenTo(this.model, "destroy", this.remove); //當模型被删除,視圖對應被移除
}
這裡對每一個todoview進行與之綁定的model資料監聽。改動,則又一次渲染;銷毀。則移除此todoview。
再看ToggleAllView的監聽:
initialize: function() {
this.listenTo(this.collection, "all", this.render); //除了todoview與todomodel一一相應
// 其它相關操作都會監聽collection
}
這個監聽更“狠”。僅僅要collection有變動,就會又一次渲染,以達到實時互動的效果。
那麼appview是怎樣管理各個子view的呢? 且看兩個appview函數:
initialize: function() {
// 初始化加入各種視圖,建立視圖并加入到父視圖指定位置
this.footer = this.$el.find('footer');
this.main = $('#main');
this.todoCollection = new todos;
inputview = new InputView({
collection: this.todoCollection
});
$("#todoapp").prepend(inputview.render().el); //加入輸入框
var toggleAllview = new ToggleAllView({
collection: this.todoCollection
});
this.main.prepend(toggleAllview.render().el); //取得資料後,再初始化
this.allCheckbox = this.$("#toggle-all")[0];
this.listenTo(this.todoCollection, "add", this.addOne);
this.listenTo(this.todoCollection, "reset", this.addAll);
this.listenTo(this.todoCollection, "all", this.render);
// 須要資料的視圖。在擷取資料後定義
this.todoCollection.fetch();
// 狀态視圖
statusview = new StatusView({
collection: this.todoCollection
});
this.footer.append(statusview.render().el); //取得資料後,再初始化
},
render: function() {
// 因為設定了all監聽全部collection的操作。故加入一個項就會被渲染一次,這保證了有修改都會得到渲染到頁面
var done = this.todoCollection.done().length;
var remaining = this.todoCollection.remaining().length;
this.allCheckbox = this.$("#toggle-all")[0];
if (this.todoCollection.length) {
//渲染時運作顯示或隐藏的代碼
this.main.show();
this.footer.show();
this.footer.html();
//假設collection為空的話,則清空footer
} else {
this.main.hide();
this.footer.hide();
}
}, // 實作總體顯示
與原子view的差别,在于appview初始化函數除了監聽collection變化外。還初始化各個原子view,并加入到指定界面位置,同一時候渲染函數依據邏輯須要。渲染整個頁面。 以上是對整個todoMVC程式的總體性架構分析。詳細互動細節可檢視樂帝源碼。