最近一段时间来,才算是真正的开始深入学习JavaScript
,收获颇丰。也首次领略了前端MVC
架构的风采,现在前端MVC
的类库和框架越来越多,在经过初步的评估之后,决定先学习备受推崇的Backbone
。
以前自己做的一些Web
应用,基本上都是按照非常传统的方式:1.服务器端渲染模板;2.利用jQuery
的ajax
进行异步数据交换。所以首次接触前端架构类的东西,难免有点无从下手。经过几天的奋战,以及参阅国外大牛们的各种Tutorial
之后,终于拨开迷雾,缕了些头绪,自己也试着从传统的方式过渡(重构)出了所谓的架构性的代码。
整个重构的过程让我受益良多,所以决定再认真的记录一遍,加深自己的印象,也再确认一遍自己是否真的搞明白了,文章应该会比较长。
首先,先上一段所谓的传统式的代码。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17$(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
应用程序的本质。
所以即便是这么一个小小的应用,逻辑和架构上都已经臃肿了,完全违反了咱们软件开发中的“单一职责原则”。如果是一个大应用,那估计就如乱麻———剪不断理还乱了。所以,改变迫在眉睫。
确实咱的要求也不高,如果把它搞成这样,其实咱就满足了:
- 在
$(document).ready
当中只保留一些应用程序的初始化代码即可,即应用的启动程序。 - 干掉乱如麻的逻辑,使得其符合咱们的“单一职责原则”,方便测试。
- 减小
ajax
和DOM
的耦合,其实也算是第2条。
OK,动手。按照最基本的重构方式,咱先把ajax
分离到一个方法里面去。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20var 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
所在的方法中,data
和success
属性仍然保留了对DOM
的依赖,于是接下来将其调整为函数的参数来传递。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23var 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()
方法加个命名空间吧。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27var 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
了。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32var 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
目前看上去都不怎么像一个对象的行为,所以继续重构之。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34var 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
的形式来完成。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22/*前面不变*/
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
,所以到此就打住了,敬请关注下回分解。