前端编程提高之旅(六)----backbone实现todoM

乐帝当年学习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程序的整体性架构分析,具体交互细节可查看乐帝源代码。

文章导航