您当时的方位:主页 > 言语编程 > JavaScript

mvc方式别离javascript开发

2014-11-02  

JavaScript MVC

中文:http://blog.youmila.com/?p=423 —from yapollo.li@gmail.com
英文:http://www.alistapart.com/articles/javascript-mvc/ —from Jonathan Snook

mvc

javascript 现已从一个“小演员”开展成为舞台的中心”人物“。它的脚印现已遍及咱们的服务器和开展计划的一览表中,而且正在持续增长中。因而咱们有必要考虑怎样才干提 高咱们的javascript代码的重用性和更简略保护性呢?或许,MVC能够给咱们一些好的提示。
MVC关于后端运用程序开发及其开发人员来说是一个了解的术语。
正在运用的相似结构比方:Struts, Ruby on Rails, 和CakePHP。 MVC 来源于用户界面的开展。借助于它布局客户端运用程序的结构。让咱们一起来看下MVC是什么。看看咱们如安在一个项目顶用mvc重写它。而且考虑一些现在已 经存在的MVC结构。
What is MVC?(MVC是什么?)
这个缩写词现已在前面提到了6次,假如你从来没有听说过,那必定刻不容缓的逍遥知道MVC代表什么,MVC代表Model-View-Controller. 它是一个将运用程序分红3个部分的规划方式:model层是数据层,view层是数据对用户的表现方式,controller层是用户交互采纳的行为动作。

追溯到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(){
var errorMessage = ‘The following errors were found:
‘;
if (document.getElementById(’email’).value.length == 0) {
errorMessage += ‘You must supply an email address
‘;
}
document.getElementById(’message’).innerHTML = errorMessage;
}

上面得这个办法能作业,可是不行灵敏,比方咱们假如想要添加个字段验证,或许另一个页面有不同表单验证。那咱们就不得不复制这个函数的大部分代码为咱们每次新添加字段验证。

Toward modularity(奔向模块化)

第一步是奔向模块化,而且别离便是在表单的字段中添加语义化的东西。

比方关于email验证的表单字段咱们能够这样做:

<input type="text" class="required email">

这样咱们的javascript会遍历一切的表单字段从class中拖出这个特点从那履行相应的程序。(这儿的class特点保存了两层意义,一个是css的款式设定另一个便是js的方针目标。多么快捷哈!~)

上面这种办法和页面的结构现已语义化符号严密环绕在一起,可是这种办法也有必定约束条件,比方没有条件判定式,而且在html符号中不能结构条件逻辑。比方:咱们说假如一个字段完结,需求另一个仅有的字段。(或许你要说能可是很笨的办法。)

<input name="other" type="checkbox" /> Other
<textarea class="dependson-other"></textarea>

在上面得这个比方傍边咱们用了前缀 dependson 指出 textarea是依托 checkbox才出现的。为了防止这种低劣的办法,咱们能够在javascript中界说这块事务逻辑。

Using JavaScript to describe things(用javascript去描绘事物)

当咱们在html中添加语义化符号以及元数据的时分,咱们终究的意图是获取信息给javascript。可是以javascript办法描绘数据是相对比较便利的。
这儿便是一个比方:(其实便是用json办法描绘)


var fields = {
'other': {
required:true
},
'additional: {
'required': {
'other':{checked:true},
'total':{between:[1,5]}
},
'only-show-if': {
'other': {checked:true}
}
}
};

在这个比方中附加字段会有几个依靠联络。依靠联络中的每一个都被描绘了。而且有各式各样的信息界说在其间。在这种情况下,附加字段需求两个字段满意条件,而且附加字段只要在用户挑选other的checkbox时才会显现出来。
在这时分,javascript被用于怎样验证发作后字段和事务逻辑的描绘界说,在这一层中咱们现已别离了一部分数据进入他们自己的目标中。可是这些验证依然期望在有用的变量数据中运用。期望在页面中能够展现出相应的过错摘要。
虽然这个比方现已有一点别离。可是这儿验证进程依然有许多的依靠性,比方:数据的验证和验证的后的报错成果显现依然存在严密的耦合。
那就让咱们考虑一下怎样才干用mvc方式构建咱们的代码。而且然后构建咱们表单验证的比方。

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字段来描绘数据的类型以及数据的限制条件。
上面这个增修改查的使命也在服务器端保存状况改动信息,在这个比方中,delete函数从本地的目标存储中删去了数据的挂号之后就将删去目标的id经过ajax发送一个删去指令恳求到服务器端,然后从服务器端删去。
在存储数据的时分,运用命名键值的办法,这是最快最有用率从目标中取回数据的办法。
这种办法一般和数据库主键相同。(上面得比方用了number类型的id)。关于一个事情日历来说,依照月份区分存储数据会更有用。这样做就不必遍历一切的事情去发现那个需求的在页面中烘托的事情了。你会发现这样干事最好的办法。

The View

在mvc方式中,view担任接纳数据而且决议数据怎样显现。view层能够用页面已存在的html,也能够从服务器端恳求一个新的html组件, 还能够自己经过dom创立新的html元素。兼并供给的数据以视图的方式显现给用户,有一点很重要,便是view层并不关怀数据来自哪里,或许怎样获取 到,它只担任取走数据运用。

View.EventsDialog = function(CalendarEvent) {
var html = ‘

{name}

‘ +

{date}

‘;
html = html.replace(//{[^/}]*/}/g, function(key){
return CalendarEvent[key.slice(1,-1)] || ”;
});
var el = document.getElementById(’eventshell’);
el.innerHTML = html;
}

var Events.data = {
‘112′: { ‘name’: ‘Party time!’, ‘date’: ‘2009-10-31′ },
‘113′: { ‘name’: ‘Pressies!’, ‘date’: ‘2009-12-25′ }
}

View.EventsDialog(Events.data['112']); // edits item 112

调查上面得程序咱们能够发现它包括三个部分:
EventsDialog 函数对CalendarEvent这个json目标的name和date特点进行了解析显现。Events的data特点存储了日历的时刻。 View.EventsDialog的调用使112显现。
这儿Events Dialog的view能够被扩展,参加一个附加函数能够使它进行有用的交互。鄙人面得比方中,Events Dialog被给了一个open办法和一个close办法。经过这样做能够使view进步自己的感知才能,一起也能够被controller层更好的运用 view层,并不需求知道它完结的细节。

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);
dialog.open();
dialog.close();

Generalizing Views(归纳观念)

使视界变得觉察到数据模型和数据检索的办法是一简略坠入的圈套. 别离这些函数不过是想让他们在其他方面能从头运用这个dialog。在这个比方傍边,假如别离了事情的数据和dialog,那么咱们能总结dialog属 于view层中,dialog不只适用events类的模型,也能运用到其他模型。

View.Dialog = function(data) {
var html = ‘

‘ + data.name + ‘

‘;
delete data.name;
for(var key in data) {
html += ‘

‘ + data[key] + ‘

‘;
}
var el = document.getElementById(’eventshell’);
el.innerHTML = html;
}

咱们现在有一个共有的办法去拜访一个恣意目标的元素,而不仅仅是事情目标。鄙人一个需求dialog的项目中,咱们能够兼并这部分代码而且运用它。
许多JavaScript结构都是以这数据不可知论来规划的。比方yui controller ,jQuery UI widgets, ExtJS, 和 Dojo Dijit 从头到位都是把通用性(泛用性)放在第一位来树立的。

Handling View methods(处理视图办法)

一般来说,view层不能运转他们自己的办法,举个比方来说,dialog(对话框)不能自己操控开关,应该由controller(操控器)–操控层来操控它是否开关。
假如一个用户点击了一个dialog中的保存按钮,这个点击动作由操控器来接纳,操控器发送一个动作来决议dialog应该做什么。或许是封闭dialog也能是显现正在处理..一旦数据保存了,ajax完结后会触发操控器宣布另一个躲藏指令来封闭dialog

无论怎样,在有些情况下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();
}

当数据在在各种情况下运用的时分这种方式的确很便利。举个比方:
咱们正在修改的日历上显现的事情,咱们点击删去按钮,现在需求去消除dialog(对话框)和日历上的事情,然后从服务器中删去该事情。

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(验证咱们的模型)

该模型确认数据是否正确或不运用的办法。它不关怀怎样出现概要的视图。它仅仅需求陈述哪些字段没有到达水平。

曾经咱们做过的那个比方傍边,有一个简略的变量“fields”存储了咱们数据模型的元数据格式,咱们能够用一个界说了解和检测被给数据的办法去扩展那个目标,该办法能够遍历一切的数据而且比对他们在内部元数据类型中界说的需求。

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 »
attention

‘ +

  • ‘ + messageData[i] + ‘

‘;
}
message += ‘

‘;
el.innerHTML = message;
}
View.Message.prototype.show() {
/* provide a slide-in animation */
}

这儿的type 是css的一个class name,给用户自界说message的类型款式进口,
这个函数遍历一切的message数据并把他们显现在页面中。

Hooking it all together with a Controller(用操控层挂起这一切流程)

咱们的model层存储了咱们的数据而且能够告知咱们数据是否有用,view层给用户显现成功或许失利的音讯,就剩余最终一步了,便是用户表单提交的时分验证表单信息。

/* use the event binding of your friendly neighbourhood
JavaScript library or whatever you like to use
*/
addEvent(document.getElementById(’myform’), ’submit’, »
MyController.validateForm);

MyController.validateForm = function(event){
var data = [];
data['other'] = document.getElementById(’other’).checked;
var invalidFields = MyModel.validate(data);

if (invalid.length) {
event.preventDefault();
// generate the view and show the message
var message = new View.Message(invalidFields, ‘error’);
message.show();
}
}

好了咱们完结了,model层和view层的办法咱们今后还能重复运用哈

Frameworks

javascript mvc正在流行起来,可是 深化的了解怎样在你的作业中运用它会更有协助。你能够自己做,也能够用现已存在的javascript mvc结构

下面是几个javascript mvc 结构:

  • JavaScriptMVC
  • SproutCore
  • TrimJunction

你的运用程序是否需求一个结构,这依靠于运用程序的杂乱性。假如它是个简略的运用程序,那么运用结构来做就不值当了。

请记住,推出自己的MVC结构并不是一种呆板,正如你想的那样它能够更灵敏。

Finally

和其他的开发相同,你必定要决议取舍,这种别离是否值当。假如仅仅一个简略程序,只要几个函数,那这种类型的别离便是显得有些过分了。假如是个杂乱的大程序那这种MVC方式的别离将会获益匪浅!