Web.js MVC between client and server
发布于 4年前 作者 willwen 4122 次浏览 最后一次编辑是 3年前

Web.js 是一个 为简化 HTTP 开发而设计的 Web Framework,它致力于以最简单的语法进行开发高性能的应用。

Web.js between client and server 是指 web.js 这个文件可以同时在客户端和服务端使用吗?

不是的。。这个先不管。。先来看看其他“无关”的。。

以下是 Wikipedia 对 MVC 的解释:

MVC模式(Model-View-Controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。






先来说说前端 MVC


现在国内来说,相对优秀的前端 MVC 架构是来自豆瓣说的 [(Backbone.js + Underscore.js) + Mustache.js + jQuery]

M - Model


Backbone.js 和 Underscore.js 是暂时来说国内公认的最好的一组 Model 框架

  1. 高性能

  2. 强自定义度

  3. 高灵活性

  4. …………


Backbone.js

//Model of Backbone

var Person = Backbone.Model.extent({
sayHello: function () {
alert('Hey, Im ' + this.get('name') + '.'); <br/> return this; <br/> }, <br/> setName: function (name) { <br/> this.set({'name': name}); <br/> alert('My name is ' + this.get('name') + '.'); <br/> return this; <br/> } <br/>}); <br/> <br/>var Will = new Person; <br/> <br/>Will.setName('Will Wen Gunn') // --> My name is Will Wen Gunn. <br/> .sayHello(); // --> Hey, Im Will Wen Gunn.

其中 Backbone.Model 可以理解为经过封装的 Class 类型,然后 Person 是一个经过 extend 拓展的自定义 Class 类型,

Will 则是继承了 Person 的一个 Model 对象。
//Model of Underscore

$.getJSON(location.origin + ‘/persons’)
.done(function (data) {
sessionStorage.persons = data;
var persons = JSON.parse(sessionStorage.persons);
// --> [{name: 'Will Wen Gunn’}, {name: 'Foo’}, {name: 'Bar’}]
$(‘body’).append(
JSON.stringify(
_.map(persons, function (person) {
if (person.name == ‘Will Wen Gunn’ || person.name !== ‘Bar’) {
return true;
}
})
)
);
});
// --> [{"name": "Will Wen Gunn"}, {"name": "Foo"}]

与其说 Underscore.js 是 Model 框架,还不如说是 Model 操作库,Underscore.js 并没有封装 Class ,而是使用 JavaScript 原有的 Array, Object, Function 类型进行操作 (其中人们比较常用 Object 和 Function 来模拟 Class)

这种 Model 会比 利用 Object 或者 Function 来定义 Class 要实际,因为这样可以很好地对对象和对象集 (Collection) 进行管理和操作。




V - View


View 更直接点地说就是我们平时所构建的 HTML ,而 MVC 其实源自于 DHTML ,W3C 的解释是动态页面(Dynamic HTML),而我更认为是 Data to HTML。

而这个正是Mustache.js诞生的原因

{{Mustache}}


var person,

tmpl-persons,
proxy = new Eventproxy(),
persons = function (data, tmpl) {
$(‘body’).append(
Mustache.to_html(tmpl, data)
);
};
proxy.assign('data’, 'tmpl’, persons);
$.getJSON(location.origin + ‘/persons’)
.done(function (data) {
persons = JSON.parse(data);
/*
* [ {Name: "Will Wen Gunn", Age: 15, Sex: "Man"},
* {Name: "Foo", Age: 15, Sex: "Man"},
* {Name: "Bar", Age: 15, Sex: "Man"} ]
/
proxy.trigger('data’, data);
});
$.get(location.origin + ‘/tmpls/persons.html’)
.done(function (data) {
tmpl-persons = data;
/

* {{#peoples}}
* {{Name}}
* {{Age}}
* {{Sex}}
* {{/peoples}}
/
proxy.trigger('tmpl’, tmpl);
});
/

* Will Wen Gunn
* 15
* Man
* Foo
* 15
* Man
* Bar
* 15
* Woman
/

这里使用到了朴灵小田同学的 EventProxy.js,相当好玩的一个小工具,但是发挥出来的作用很强阿。

它能让几个异步请求并行处理,最后集中处理。

https://github.com/JacksonTian/eventproxy

另外也还有一个来自国外的,类似的东西,叫Step,它也让异步函数分开,但是是串列的队列式,所以会产生阻塞,小问并不建议使用。




C - Controller


这个恐怕是争议最大的一块了,有人推崇jQuery,有人推崇YUI,有人推崇MooTools……

其实这个并没有什么太大关系,只要是有这样的能力的,哪种 JavaScript Library 都没所谓的。

我这里用jQuery把上面的这些M,V整合起来:
var Person = Backbone.Model.extent({

sayHello: function () {
alert('Hey, Im ' + this.get('name') + '.'); <br/> return this; <br/> }, <br/> setName: function (name) { <br/> this.set({'name': name}); <br/> alert('My name is ' + this.get('name') + '.'); <br/> return this; <br/> } <br/> }), <br/> Persons = Backbone.Collection.extend({ <br/> model: Person, <br/> sayHello: function () { <br/> this.each(function (Person) { <br/> $('body').append('Hey, Im ' + Person.get(‘name’) + ‘.’);
});
},
sayName: function () {
this.each(function (Person) {
$(‘body’).append('My name is ' + Person.get(‘name’) + ‘.’);
});
}
});
var CNodejs = new Persons;
var persons,
tmpl-persons;
$.get(location.origin + ‘/persons’)
.done(function (data) {
persons = JSON.parse(data);
})
.get(location.origin + ‘/tmpls/persons.html’)
.done(function () {
tmpl-persons = JSON.parse(data);
});
CNodejs.add(persons.peoples);
$(‘body’).append(
Mustache.to_html(
tmpl-persons,
{ peoples: CNodejs.toJSON() }
)
);





Node.js MVC


M - Model, V - View


其实 Backbone.js, Underscore.js 和 Mustache.js 在 Node.js 上的用法是和前端一模一样的,所以我就不多介绍了。

C - Controller


来看看 Web.js 的 Router :
var urlRouter = {

'^(\d{4})/(\d{2})/(\d{2})/(.
).jpg’: '$1-$2-$3-$4.jpg’,
'google’: 'http://www.google.com’,
'iwillwen’: ‘http://www.iwillwen.com
},
getRouter = {
'^getsomthing’: function(req, res, qs) {
res.sendJSON(qs);
},
'^car’: function(req, res, qs) {
switch (qs.action) {
case 'foo’:
res.send(‘Your action is foo’);
break;
case 'bar’:
res.send(‘Your action is bar’);
break;
}
}
},
postRouter = {
'^postsomthing’: function(req, res, data) {
res.sendJSON(qs);
},
'^car’: function(req, res, data) {
switch (data.action) {
case 'foo’:
res.send(‘Your action is foo’);
break;
case 'bar’:
res.send(‘Your action is bar’);
break;
}
}
};

web.run(urlRouter, 80)
.get(getRouter)
.post(postRouter);

console.log(‘The app is running on http://localhost’);

这个和Express有点区别。

如果结合数据库的 Node.js MVC,Web.js该怎么写呢
var web =require(‘webjs’),

mongo = require(‘mongoskin’),
Backbone = require(‘backbone’),
Underscore = require(‘underscore’),
eventproxy = require(‘EventProxy.js’),
db = mongo.db(‘localhost:27017/blog’),
posts = db.collection(‘posts’),
metas = db.collection(‘metas’),

var urlRouter = {
'^page/(\d)': 'page.html’,
'^(.)': ‘post.html’
},
getRouter = {
'init’: function (req, res, qs) {
var proxy = new eventproxy.EventProxy(),
init = function (title, description, posts) {
var obj = {
title: title,
description: description,
posts: posts
};
res.sendJSON(obj);
};
proxy.assign('title’, 'description’, 'posts’, init);
metas.findOne({type: 'title’}, function (err, title) {
proxy.trigger('title’, title);
});
metas.findOne({type: 'description’}, function (err, description) {
proxy.trigger('description’, description);
});
posts.findTop10(function (err, posts) {
proxy.trigger('posts’, posts);
});
},
'getPost’: function (req, res, qs) {
posts.findOne(qs, function (err, post) {
res.sendJSON(post);
});
}
},
postRouter = {
'setMeta’: function (req, res, data) {
metas.update(
{type: data.type},
data,
{upsert: true},
function (err) {
if (err) return res.send('Set failed.
' + err);
res.send(‘Set successed.’);
}
);
},
'post’: function (req, res, data) {
posts.update(
{title: data.title},
data,
{upsert: true},
function (err) {
if (err) return res.send('Post failed.
' + err);
res.send(‘Post successed’);
}
);
}
};
web.run(urlRouter, 80)
.get(getRouter)
.post(postRouter);





最后来讲讲

Web.js between client and server


来看看 Web.js for client (仍在编写中) 的用法
(function ($) {

//loaded /js/web-client.js
var getRouter = {
‘getsomething’ : function () {
var proxy = new Eventproxy(),
post = function (data, tmpl) {
$(‘body’).append(
Mustache.to_html(tmpl, data)
);
};
proxy.assign('data’, 'tmpl’, init);
web.getData('post’, {}, function (data) {
proxy.trigger('data’, data);
});
web.getTmpl('post’, function (tmpl) {
proxy.trigger('tmpl’, tmpl);
});
}
};
web.conn(location.origin)
.get(getRouter);
})(jQuery);





这时候大家可能会问了,Web.js 的 MVC 究竟异样在哪里呢?

前端 MVC 的 M 其实和后端 MVC 的 M 本来是不可能一起使用的,因为如果由后端进行模板渲染,前端就没有渲染的必要,那么前端 MVC就不成立了。同理,如果让前端进行模板渲染,后端也就不存在 MVC 的概念。

如果要前后端的 MVC 同时存在要怎么做呢,其实很简单,就是让客户端进行性能·评估。

比如说 IE6、7 这样的低性能浏览器,Web.js for client 会让服务器先进行 Data to HTML 渲染,然后传输再到客户端,如果是Chrome,FireFox,Safari 和 Opera 等高性能浏览器,则选择在客户端进行渲染,减轻传输荷载。

另外 Web.js 默认会开启缓存应用加速机制 (DOM Storage,Cookies,Buffer,Object……),让一部分数据先存入缓存,让短时间内再次发出的请求从缓存中获取。减少 LAN 资源和数据库资源的损耗。

JavaScript 是一门建立在静态页面上的动态脚本语言,Ajax的普及和发展,使它完全可以完成一些静态页面做不到的事情,比如像PHP自身的文件响应机制 (相比这也是 PHP 吸引人的一个终于优点)。

上面的这一段代码就演示了一个静态页面通过 Web.js 进行 URL action router 识别,并向服务器请求数据和模板,然后在 DOM 中插入渲染得到的 HTML。



两个 Web.js 之间是可以无缝对接的,开发者无须设置太多。

当然 Web.js for client 也是支持 Express 等其他 Server-side 开发框架的。




好,最后来放一个完整的 Web.js MVC Router 代码
//server.js

var web =require(‘webjs’),
mongo = require(‘mongoskin’),
Backbone = require(‘backbone’),
Underscore = require(‘underscore’),
eventproxy = require(‘EventProxy.js’),
db = mongo.db(‘localhost:27017/blog’),
posts = db.collection(‘posts’),
metas = db.collection(‘metas’),

var urlRouter = {
'^(.
)': ‘page.html’
},
getRouter = {
'init’: function (req, res, qs) {
var proxy = new eventproxy.EventProxy(),
init = function (title, description, posts) {
var obj = {
title: title,
description: description,
posts: posts
};
if (qs.render) {
res.send(
web.render('init’, obj)
);
} else {
res.sendJSON(obj);
}
};
proxy.assign('title’, 'description’, 'posts’, init);
metas.findOne({type: 'title’}, function (err, title) {
proxy.trigger('title’, title);
});
metas.findOne({type: 'description’}, function (err, description) {
proxy.trigger('description’, description);
});
posts.findTop10(function (err, posts) {
proxy.trigger('posts’, posts);
});
},
'getpost’: function (req, res, qs) {
posts.findOne(qs ,function (err, post) {
if (qs.render) {
res.send(
web.render('post’, post)
);
} else {
res.sendJSON(post);
}
});
}
};
web.run(urlRouter, 80)
.get(getRouter)
.set('tmplDir’, ‘tmpls’);

//page.html - client.js
(function ($) {
var getRouter = {
‘/’ : function () { //init
var proxy = new Eventproxy(),
init = function (data, tmpl) {
$(‘body’).append(
Mustache.to_html(tmpl, data)
);
};
proxy.assign('data’, 'tmpl’, init);
web.getData('init’, {}, function (data) {
proxy.trigger('data’, data);
});
web.getTmpl('init’, function (tmpl) {
proxy.trigger('tmpl’, tmpl);
});
}
'^(.)' : function (action) { // action --> (.)
var proxy = new Eventproxy(),
post = function (data, tmpl) {
$(‘body’).append(
Mustache.to_html(tmpl, data)
);
};
proxy.assign('data’, 'tmpl’, init);
web.getData('post’, {title: action}, function (data) {
proxy.trigger('data’, data);
});
web.getTmpl('post’, function (tmpl) {
proxy.trigger('tmpl’, tmpl);
});
}
};
web.conn(location.origin)
.get(getRouter)
})(jQuery);

9 回复

恭喜小问~~~快请客!

不错,小问
把一些优秀的框架也做了简介,学习了!

看到这样的代码:

metas.findOne({type: 'title’}, function (err, title) {
proxy.trigger('title’, title);
});
metas.findOne({type: 'description’}, function (err, description) {
proxy.trigger('description’, description);
});
posts.findTop10(function (err, posts) {
proxy.trigger('posts’, posts);
});

我觉得没用Jscex真是可惜啊:

var title = $await(metas.findOneAsync({type: "title"}));
var description = $await(metas.findOneAsync({type: 'description’}));
var posts = $await(metas.findOneAsync());

现在文章里的代码,一点错误处理都没有的,按理来说应该到处是 if (err) { … } 才对。用Jscex的话这种到处都必须写一下的错误处理方式就没必要了。

@老赵 这个只是DEMO…没必要太过于细致吧。。

作者15岁?太牛B了吧

[…] Web.js MVC between client and server        […]

[…] Web.js MVC between client and server. Like this:LikeBe the first to like this post. This entry was posted in Javascript MVC. Bookmark the permalink. ← [翻译]用 Backbone.js, underscore.js 和 jQuery 创建页面应用 […]

[…] 本文直接ネタ小问的文章,源码都是照抄来的,并换成了等价的 Eisa 形式。我省略了包导入的部分。 […]

回到顶部