Web.js 是一个 为简化 HTTP 开发而设计的 Web Framework,它致力于以最简单的语法进行开发高性能的应用。
Web.js between client and server 是指 web.js 这个文件可以同时在客户端和服务端使用吗?
不是的。。这个先不管。。先来看看其他“无关”的。。
以下是 Wikipedia 对 MVC 的解释:
先来说说前端 MVC
现在国内来说,相对优秀的前端 MVC 架构是来自豆瓣说的 [(Backbone.js + Underscore.js) + Mustache.js + jQuery]
M – Model
Backbone.js 和 Underscore.js 是暂时来说国内公认的最好的一组 Model 框架
- 高性能
- 强自定义度
- 高灵活性
- …………
//Model of Backbone
var Person = Backbone.Model.extent({
sayHello: function () {
alert('Hey, I`m ' + this.get('name') + '.');
return this;
},
setName: function (name) {
this.set({'name': name});
alert('My name is ' + this.get('name') + '.');
return this;
}
});
var Will = new Person;
Will.setName('Will Wen Gunn') // --> My name is Will Wen Gunn.
.sayHello(); // --> Hey, I`m 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, I`m ' + this.get('name') + '.');
return this;
},
setName: function (name) {
this.set({'name': name});
alert('My name is ' + this.get('name') + '.');
return this;
}
}),
Persons = Backbone.Collection.extend({
model: Person,
sayHello: function () {
this.each(function (Person) {
$('body').append('Hey, I`m ' + 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);