|
中文:http://blog.youmila.com/?p=423 —from yapollo.li@gmail.com
javascript 已经从一个“小演员”发展成为舞台的中心”人物“。它的足迹已经遍布我们的服务器和发展计划的一览表中,并且正在持续增长中。因此我们必须思考怎样才能提 高我们的javascript代码的重用性和更容易维护性呢?或许,MVC能够给我们一些好的提示。 追溯到1978年在Xeroc PARC, Trygve Reenskau发表了 recalled the origin of the MVC concept (PDF):(这篇文章成为了MVC的起源) 这部分原文我就不翻译了哈(保留原味的好哈): There are four roles in this user interaction paradigm. The human User has a mental model of the information he is currently working with. The object playing the Model role is the computer’s internal and invisible representation of this information. The computer presents different aspects of the information through objects playing the View role, several Views of the same model objects can be visible on the computer screen simultaneously. Objects playing the Controller role translate User commands into suitable messages for the View or Model objects as needed. 换句话说,用户在做某件事情时,这件事情被转到controller这边,并且controller知道下一步去做什么,一般来说 controller会从model层这边请求数据,并且把获取到的数据放到view层并且显示给用户。但是这样的划分层结构,对于一个网站或者是web 应用程序来说意味着什么呢? Foundations(基础)静态文档时web页面的基础,给我们服务的每个页面都反映了他们在服务器那一刻的信息状态。但是我们得到不止是原始数据,而是包含原始数据的xhtml或者html数据,并且通过已经预定义的css渲染后的漂亮的页面。 多年前,如果你想去修改原始数据,服务端必须提供一个可以文本输入的页面去做改动。那时候我们把改动后的信息发送给服务端,并且等待服务端返回ok 的反馈后才能搞定。每次都是完整的请求一个新的页面,然后在等待服务器反馈,这样让我们用户感到乏味不堪,甚至当你出现错误的时候,还需要重新键入原来输 入的信息。 The knight in shining armor (铠甲骑士)后来,web早期的黑暗迎来了他们的拯救者,铠甲骑士 –javascript和ajax。 他们结束以前的整个页面请求的方式,可以单个元素发送用户请求到后端服务器。 并且也允许用户页面发送请求的时候,继续响应用户的其他操作。 现在我们需要在javascript和ajax的发展和运用中采用MVC的模式分离代码: 比如说:在某些情况下,分离可能是不需要的,甚至某些情况下,分离会造成很多不必要的程序冗长。当我们的应用程序便得越来越复杂,需要 javascript在网站的多数部分的交互操作的时候。我们把javascript分离进入MVC模式能够产生出更多元化,更重复利用的代码。 Structuring our code(构造我们的代码)javscript是个傻瓜,他不会明白html将要告诉用户什么或者用户想在这个页面完成什么。所以我们作为开发者,就必须告诉我们的javascript,用户的输入意味着什么。 思考下面的例子,如果我们需要验证表单中的数据,我们可以设置一个事件来处理这个任务,在这个任务中,事件处理函数去遍历表单中的字段列表,并且确定怎样去反馈出错误的结果。 function validateForm(){ 上面得这个方法能工作,但是不够灵活,比如我们如果想要增加个字段验证,或者另一个页面有不同表单验证。那我们就不得不拷贝这个函数的大部分代码为我们每次新增加字段验证。 Toward modularity(奔向模块化)第一步是奔向模块化,并且分离就是在表单的字段中添加语义化的东西。 比如对于email验证的表单字段我们可以这样做: <input type="text" class="required email">这样我们的javascript会遍历所有的表单字段从class中拖出这个属性从那执行相应的程序。(这里的class属性保留了双重含义,一个是css的样式设定另一个就是js的目标对象。多么便捷哈!~) 上面这种方式和页面的结构已经语义化标记紧密缠绕在一起,但是这种方式也有一定限制条件,比如没有条件判定式,而且在html标记中不能构造条件逻辑。比如:我们说如果一个字段完成,需要另一个唯一的字段。(可能你要说能但是很笨的方法。) 在上面得这个例子当中我们用了前缀 dependson 指出 textarea是依靠 checkbox才出现的。为了避免这种拙劣的方法,我们可以在javascript中定义这块业务逻辑。 Using javascript to describe things(用javascript去描述事物)当我们在html中增加语义化标记以及元数据的时候,我们最终的目的是获取信息给javascript。但是以javascript方式描述数据是相对比较方便的。 在这个例子中附加字段会有几个依赖关系。依赖关系中的每一个都被描述了。并且有各种各样的信息定义在其中。在这种情况下,附加字段需要两个字段满足条件,并且附加字段只有在用户选择other的checkbox时才会显示出来。 The Model既然mvc有三个主要组成部分,那么我们的程序也要相应的划分成至少3个主要对象。 分离model层进入它自己的对象是比较容易的,正如我们早期看到那个表单验证的例子,这个常常发生的很自然。 让我们来看那下另外一个例子吧。假设我们有一个日历事件,这个事件的数据将会存储在它自身的对象中,增加到对象的中的方法是抽象了直接与数据交互的过程。这些方法经常被增删改查的任务调用,比如:创建,读取,更新,删除等操作。 var Events = {
get: function (id) { return this.data[id]; }, del: function (id) { delete this.data[id]; AjaxRequest.send(’/events/delete/’ + id); }, data:{ ‘112′: { ‘name’: ‘Party time!’, ‘date’: ‘2009-10-31′ }, ‘113′: { ‘name’: ‘Pressies!’, ‘date’: ‘2009-12-25′ } } metadata: { ‘name’: { ‘type’:'text’, ‘maxlength’:20 }, ‘date’: { ‘type’:'date’, ‘between’:['2008-01-01','2009-01-01'] } } } 我们需要一个方法去描述数据。所以我们增加这个metadata字段来描述数据的类型以及数据的限定条件。 The View在mvc模式中,view负责接收数据并且决定数据如何显示。view层可以用页面已存在的html,也可以从服务器端请求一个新的html组件, 还可以自己通过dom创建新的html元素。合并提供的数据以视图的形式显示给用户,有一点很重要,就是view层并不关心数据来自哪里,或者怎么获取 到,它只负责取走数据使用。 View.EventsDialog = function(CalendarEvent) {
var html = ‘ {name}‘ + {date}
‘; var Events.data = { View.EventsDialog(Events.data['112']); // edits item 112 观察上面得程序我们能够发现它包含三个部门: View.EventsDialog = function(CalendarEvent){ … }
View.EventsDialog.prototype.open = function(){ document.getElementById(’eventshell’).style.display = ‘block’; } View.EventsDialog.prototype.close = function(){ document.getElementById(’eventshell’).style.display = ‘none’; } var dialog = new View.EventsDialog(eventObject); Generalizing Views(概括观点)使视野变得觉察到数据模型和数据检索的方法是一容易坠入的陷阱. 分离这些函数不过是想让他们在其他方面能重新使用这个dialog。在这个例子当中,如果分离了事件的数据和dialog,那么我们能总结dialog属 于view层中,dialog不只适用events类的模型,也能应用到其他模型。 View.Dialog = function(data) {
var html = ‘ ‘ + data.name + ‘‘; ‘ + data[key] + ‘
‘; 我们现在有一个共有的方法去访问一个任意对象的元素,而不仅仅是事件对象。在下一个需要dialog的项目中,我们可以合并这部分代码并且使用它。 Handling View methods(处理视图方法)一般来说,view层不能运行他们自己的方法,举个例子来说,dialog(对话框)不能自己控制开关,应该由controller(控制器)–控制层来控制它是否开关。 无论怎样,在有些情况下view层也能够运行它自己的方法。比如一个view页面中有一个以slide形式展示的输入框,并且允许用户提取里面内容 的时候,view会自己处理交互操作的,让slide的内容显示出来,这个时候就不需要controller(控制器)来操作这个交互了。 The Controller现在,从 model层到view层数据是怎样获取到得呢?这就是通过controller层做的。controller激活是在事件发生以后,多半是在页面载入或 者用户发起的行为事件。一个事件处理程序被分配到一个controller(控制器)层的方法是做用户的竞标。 Controllers.EventsEdit = function(event) {
/* event is the javascript event, not our calendar event */ // grab the event target id, which stores the id var id = event.target.id.replace(/[^d]/g, ”); var dialog = new View.Dialog( Events.get(id) ); dialog.open(); } 当数据在在各种情况下使用的时候这种模式确实很方便。举个例子: Controller.EventsDelete = function(event) {
var id = event.target.id.replace(/[^d]/g, ”); View.Calendar.remove(id); Events.del(id); dialog.close(); } controller的行为就变得相对容易理解和简单了。这是建立可维护应用程序的关键。 Break it up(分解它)现在我们了解了怎样去分解我们的代码到他们的构成部分。让我们重新回来开始部分的表单验证的例子,我们怎样才能用MVC模式去设计它以达到最大灵活性。 Validating our Model(验证我们的模型)该模型确定数据是否正确或不使用的方法。它不关心如何呈现概要的视图。它只是需要报告哪些字段没有达到水平。 以前我们做过的那个例子当中,有一个简单的变量“ var MyModel = {
validate: function(data) { var invalidFields = []; for (var i = 0; i < data.length; i++) { if (this.metadata[data.key].required && !data.value) { invalidFields[invalidFields.length] = { field: data.key, message: data.key + ‘ is required.’ }; } } return invalidFields; }, metadata: { ‘other’: {required:true} } } 为了使我们的数据有效,我们提供一个数组key /value (键/值)对。key就是名字,value就是用户键入的字段的内容。 var data = [
{'other':false} ]; var invalid = MyModel.validate(data); 我们的无效变量现在包含任何没有验证字段列表,现在我们要传递这些数据到view层中在页面中显示这些错误。 Presenting the invalid fields(显示无效的字段)在这种情况下,我们需要在页面中显示一条错误信息。这个显示工作由view层完成,显示的数据来自controller提供。view层会用这些数据构建一个错误信息显示给用户,我们已经写好了,并且可以在许多情况下使用它。 View.Message = function(messageData, type){
var el = document.getElementById(’message’); el.className = type; var message = ‘ We have something to bring to your »
|
文明社会,从理性发言开始。谢绝地域攻击。