標籤:des style blog http color io os ar java
樂帝當年學習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程式的整體性架構分析,具體互動細節可查看樂帝原始碼。
前端編程提高之旅(六)----backbone實現todoMVC