`

Backbone之旅:前端MVC架构初体验(上)

    博客分类:
  • SSH
 
阅读更多

原文链接:http://heroicyang.com/2012/08/08/to-backbone-tutorial-1/

最近一段时间来,才算是真正的开始深入学习JavaScript,收获颇丰。也首次领略了前端MVC架构的风采,现在前端MVC的类库和框架越来越多,在经过初步的评估之后,决定先学习备受推崇的Backbone

以前自己做的一些Web应用,基本上都是按照非常传统的方式:1.服务器端渲染模板;2.利用jQueryajax进行异步数据交换。所以首次接触前端架构类的东西,难免有点无从下手。经过几天的奋战,以及参阅国外大牛们的各种Tutorial之后,终于拨开迷雾,缕了些头绪,自己也试着从传统的方式过渡(重构)出了所谓的架构性的代码。

整个重构的过程让我受益良多,所以决定再认真的记录一遍,加深自己的印象,也再确认一遍自己是否真的搞明白了,文章应该会比较长。

 

首先,先上一段所谓的传统式的代码。

$(function(){
  $('#new-todo form').submit(function(e){
    e.preventDefault();
    var that = this;

    $.ajax({
      url: '/add',
      type: 'POST',
      dataType: 'json',
      data: { todoContent: $(this).find('textarea').val() },
      success: function(data){
        $('#todo-list ul').append('<li>' + data.todoContent + '</li>');
        $(that).find('textarea').val('');
      }
    });
  });
});

另外,也上一张关于这篇文章中涉及到的HTML结构图,方便参照。由于文章稍长,我想如果直接在这里插图的话会影响阅读,所以就只给出图片链接了。

上面的一段代码我想应该都是大家非常熟悉的做法,因为我是一个伪前端攻城湿,所以我以前的代码中无不充斥着类似的、一堆一堆这样的代码。看上去貌似挺好的啊,也没啥问题,程序跑得倍儿棒。但是就这么短短的一段代码,它可干了不少事情:监听页面事件、用户事件、网络事件,接收用户的输入、执行网络的I/O、解析服务端返回的数据、动态生成HTML结构,可谓是包罗万象啊,就这么短短的一段代码就解释了整个Web应用程序的本质。

所以即便是这么一个小小的应用,逻辑和架构上都已经臃肿了,完全违反了咱们软件开发中的“单一职责原则”。如果是一个大应用,那估计就如乱麻———剪不断理还乱了。所以,改变迫在眉睫。

确实咱的要求也不高,如果把它搞成这样,其实咱就满足了:

  1. $(document).ready当中只保留一些应用程序的初始化代码即可,即应用的启动程序。
  2. 干掉乱如麻的逻辑,使得其符合咱们的“单一职责原则”,方便测试。
  3. 减小ajaxDOM的耦合,其实也算是第2条。

OK,动手。按照最基本的重构方式,咱先把ajax分离到一个方法里面去。

var addTodo = function(){
  $.ajax({
    url: '/add',
    type: 'POST',
    dataType: 'json',
    data: { todoContent: $('#new-todo').find('textarea').val() },
    success: function(data){
      $('#todo-list ul').append('<li>' + data.todoContent + '</li>');
      $('#new-todo').find('textarea').val('');
    }
  });
};

$(function(){
  $('#new-todo form').submit(function(e){
    e.preventDefault();
    
    addTodo();
  });
});

但是,在ajax所在的方法中,datasuccess属性仍然保留了对DOM的依赖,于是接下来将其调整为函数的参数来传递。

var addTodo = function(options){
  $.ajax({
    url: '/add',
    type: 'POST',
    dataType: 'json',
    data: { todoContent: options.todoContent },
    success: options.success
  });
};

$(function(){
  $('#new-todo form').submit(function(e){
    e.preventDefault();
    
    addTodo({
      todoContent: $('#new-todo').find('textarea').val(),
      success: function(data){
        $('#todo-list ul').append('<li>' + data.todoContent + '</li>');
        $('#new-todo').find('textarea').val('');
      }
    });
  });
});

好像OK了,不过此时addTodo()方法暴露在全局环境内,任何人都可以呼之欲来。我可不想当屌丝,作为一个富有上进心的、想成为一个合格前端攻城湿的我,还是给addTodo()方法加个命名空间吧。

var TodoList = function(){};

TodoList.prototype.add = function(options){
  $.ajax({
    url: '/add',
    type: 'POST',
    dataType: 'json',
    data: { todoContent: options.todoContent },
    success: options.success
  });
};

$(function(){
  var todoList = new TodoList();

  $('#new-todo form').submit(function(e){
    e.preventDefault();
    
    todoList.add({
      todoContent: $('#new-todo').find('textarea').val(),
      success: function(data){
        $('#todo-list ul').append('<li>' + data.todoContent + '</li>');
        $('#new-todo').find('textarea').val('');
      }
    });
  });
});

现在submit事件只依赖一个todoList变量了,而且最重要的是现在的submit事件中只关注DOM操作了,干脆大刀阔斧的把它移到外层去。于是咱们引入视图View了。

var TodoList = function(){};

TodoList.prototype.add = function(options){
  $.ajax({
    url: '/add',
    type: 'POST',
    dataType: 'json',
    data: { todoContent: options.todoContent },
    success: options.success
  });
};

var NewTodoView = function(options){
  var todoList = options.todoList;

  $('#new-todo form').submit(function(e){
    e.preventDefault();
    
    todoList.add({
      todoContent: $('#new-todo').find('textarea').val(),
      success: function(data){
        $('#todo-list ul').append('<li>' + data.todoContent + '</li>');
        $('#new-todo').find('textarea').val('');
      }
    });
  });
};

$(function(){
  var todoList = new TodoList();
  new NewTodoView({ todoList: todoList });
});

恩,现如今$(document).ready中就简洁得只剩我们之前所说的应用启动代码了。虽然代码已经组件化了,也工作得很好,但是仍然有需要重构的地方。NewTodoView目前看上去都不怎么像一个对象的行为,所以继续重构之。

var TodoList = function(){};

TodoList.prototype.add = function(options){
  $.ajax({
    url: '/add',
    type: 'POST',
    dataType: 'json',
    data: { todoContent: options.todoContent },
    success: options.success
  });
};

var NewTodoView = function(options){
  this.todoList = options.todoList;

  $('#new-todo form').submit($.proxy(this.addTodo, this));
};

NewTodoView.prototype.addTodo = function(e){
  e.preventDefault();
    
  this.todoList.add({
    todoContent: $('#new-todo').find('textarea').val(),
    success: function(data){
      $('#todo-list ul').append('<li>' + data.todoContent + '</li>');
      $('#new-todo').find('textarea').val('');
    }
  });
};

$(function(){
  var todoList = new TodoList();
  new NewTodoView({ todoList: todoList });
});

这里用到了jQuery中的$.proxy()方法来解决this作用域的问题,玩JavaScript的童鞋们应该都很了解作用域这个东东。接下来,咱干点有关洁癖的事情,鉴于要保证代码的清晰、方便阅读,咱把success里面的行为采用callback的形式来完成。

/*前面不变*/
NewTodoView.prototype.addTodo = function(e){
  e.preventDefault();
  var that = this;

  this.todoList.add({
    todoContent: $('#new-todo').find('textarea').val(),
    success: function(data){
      that.appendTodo(data.todoContent);
      that.clearTextArea();
    }
  });
};

NewTodoView.prototype.appendTodo = function(todoContent){
  $('#todo-list ul').append('<li>' + todoContent + '</li>');
};

NewTodoView.prototype.clearTextArea = function(){
  $('#new-todo').find('textarea').val('');
};
/*后面也不变*/

至此,重构的第一个版本其实就算得上大功告成了,已经达到前面提出的三大方针政策。文章果然比较长,所以我决定还是分成了上、下两节,当前这篇中完全没涉及到backbone,所以到此就打住了,敬请关注下回分解。

 

另一篇更高层次上讲前端MVC的文章是:http://www.slideshare.net/enmaai/mvc-8825384

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics