天天看點

前端程式設計提高之旅(六)----backbone實作todoMVC

            樂帝當年學習backbone時。最開始是看官網todoMVC的實作。後來了解到requireJS便于管理JS代碼。就對官網代碼做了requireJS管理。但此時樂帝感覺此時的todoMVC仍然不夠簡明,為了加深對MVC架構的了解。樂帝對原有appview代碼進行了重構,将相關顯示子產品單獨提取出自稱view。實作view原子化。樂帝已經将這個項目上傳(下載下傳位址)。

    增加requireJS的檔案夾結構:

前端程式設計提高之旅(六)----backbone實作todoMVC

   這裡主要用到templates用于放置view相應的模闆。views則相應backbone中view檔案。假設說backbone是前端MVC,那麼model是對資料建立模型。collection則是對model統一管理,view則起到控制器的作用,用于填充資料到模闆,并渲染模闆到顯示。model、collection起到M作用。view起到C的作用,模闆則起到V的作用。

   然後我們看一下todoMVC的效果圖:

前端程式設計提高之旅(六)----backbone實作todoMVC

    從終于效果圖。我們能夠分析出,要對原有appview中解耦出原子view。就須要推斷出哪些是原子view。原子view須要具備兩點:

  • 具有動态互動效果
  • 與其它頁面部分獨立

   當然這裡的原子view定義還值得商榷。樂帝依據以上兩個原則對view進行了又一次劃分。    且看views檔案夾結構:

前端程式設計提高之旅(六)----backbone實作todoMVC

   相應模闆檔案夾結構:

前端程式設計提高之旅(六)----backbone實作todoMVC

   須要注意的是,這裡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程式的總體性架構分析。詳細互動細節可檢視樂帝源碼。