Web.js 是一个 为简化 HTTP 开发而设计的 Web Framework,它致力于以最简单的语法进行开发高性能的应用。
<br/>
<br/>Web.js between client and server 是指 web.js 这个文件可以同时在客户端和服务端使用吗?
<br/>
<br/>不是的。。这个先不管。。先来看看其他“无关”的。。
<br/>
<br/>以下是 Wikipedia 对 MVC 的解释:
<br/><blockquote>MVC模式(Model-View-Controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。</blockquote>
<br/><!–more–>
<br/>
<br/><hr />
<br/>
<br/><h1>先来说说前端 MVC</h1>
<br/>现在国内来说,相对优秀的前端 MVC 架构是来自豆瓣说的 [(Backbone.js + Underscore.js) + Mustache.js + jQuery]
<br/><h2>M - Model</h2>
<br/>Backbone.js 和 Underscore.js 是暂时来说国内公认的最好的一组 Model 框架
<br/><ol>
<br/> <li>高性能</li>
<br/> <li>强自定义度</li>
<br/> <li>高灵活性</li>
<br/> <li>…………</li>
<br/></ol>
<br/><div><img src=“http://documentcloud.github.com/backbone/docs/images/backbone.png” alt=“Backbone.js” /></div>
<br/><pre escaped=“true” lang=“javascript” line=“1”>//Model of Backbone
<br/>var Person = Backbone.Model.extent({
<br/> sayHello: function () {
<br/> 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, I
m Will Wen Gunn.</pre>
<br/>其中 Backbone.Model 可以理解为经过封装的 Class 类型,然后 Person 是一个经过 extend 拓展的自定义 Class 类型,
<br/>
<br/>Will 则是继承了 Person 的一个 Model 对象。
<br/><pre escaped=“true” lang=“javascript” line=“1”>//Model of Underscore
<br/>$.getJSON(location.origin + ‘/persons’)
<br/> .done(function (data) {
<br/> sessionStorage.persons = data;
<br/> var persons = JSON.parse(sessionStorage.persons);
<br/> // --> [{name: ‘Will Wen Gunn’}, {name: ‘Foo’}, {name: ‘Bar’}]
<br/> $(‘body’).append(
<br/> JSON.stringify(
<br/> _.map(persons, function (person) {
<br/> if (person.name == ‘Will Wen Gunn’ || person.name !== ‘Bar’) {
<br/> return true;
<br/> }
<br/> })
<br/> )
<br/> );
<br/> });
<br/>// --> [{“name”: “Will Wen Gunn”}, {“name”: “Foo”}]</pre>
<br/>与其说 Underscore.js 是 Model 框架,还不如说是 Model 操作库,Underscore.js 并没有封装 Class ,而是使用 JavaScript 原有的 Array, Object, Function 类型进行操作 (其中人们比较常用 Object 和 Function 来模拟 Class)
<br/>
<br/>这种 Model 会比 利用 Object 或者 Function 来定义 Class 要实际,因为这样可以很好地对对象和对象集 (Collection) 进行管理和操作。
<br/>
<br/><hr />
<br/>
<br/><h2>V - View</h2>
<br/>View 更直接点地说就是我们平时所构建的 HTML ,而 MVC 其实源自于 DHTML ,W3C 的解释是动态页面(Dynamic HTML),而我更认为是 Data to HTML。
<br/>
<br/>而这个正是Mustache.js诞生的原因
<br/><h1><strong>{{Mustache}}</strong></h1>
<br/><pre escaped=“true” lang=“javascript” line=“1”>var person,
<br/> tmpl-persons,
<br/> proxy = new Eventproxy(),
<br/> persons = function (data, tmpl) {
<br/> $(‘body’).append(
<br/> Mustache.to_html(tmpl, data)
<br/> );
<br/> };
<br/>proxy.assign(‘data’, ‘tmpl’, persons);
<br/>$.getJSON(location.origin + ‘/persons’)
<br/> .done(function (data) {
<br/> persons = JSON.parse(data);
<br/> /*
<br/> * [ {Name: “Will Wen Gunn”, Age: 15, Sex: “Man”},
<br/> * {Name: “Foo”, Age: 15, Sex: “Man”},
<br/> * {Name: “Bar”, Age: 15, Sex: “Man”} ]
<br/> /
<br/> proxy.trigger(‘data’, data);
<br/> });
<br/>$.get(location.origin + ‘/tmpls/persons.html’)
<br/> .done(function (data) {
<br/> tmpl-persons = data;
<br/> /
<br/> * {{#peoples}}
<br/> * {{Name}}
<br/> * {{Age}}
<br/> * {{Sex}}
<br/> * {{/peoples}}
<br/> /
<br/> proxy.trigger(‘tmpl’, tmpl);
<br/> });
<br/>/
<br/> * Will Wen Gunn
<br/> * 15
<br/> * Man
<br/> * Foo
<br/> * 15
<br/> * Man
<br/> * Bar
<br/> * 15
<br/> * Woman
<br/> /</pre>
<br/>这里使用到了朴灵小田同学的 EventProxy.js,相当好玩的一个小工具,但是发挥出来的作用很强阿。
<br/>
<br/>它能让几个异步请求并行处理,最后集中处理。
<br/>
<br/><a title=“https://github.com/JacksonTian/eventproxy” href=“https://github.com/JacksonTian/eventproxy” target="_blank">https://github.com/JacksonTian/eventproxy</a>
<br/>
<br/>另外也还有一个来自国外的,类似的东西,叫Step,它也让异步函数分开,但是是串列的队列式,所以会产生阻塞,小问并不建议使用。
<br/>
<br/><hr />
<br/>
<br/><h2>C - Controller</h2>
<br/>这个恐怕是争议最大的一块了,有人推崇jQuery,有人推崇YUI,有人推崇MooTools……
<br/>
<br/>其实这个并没有什么太大关系,只要是有这样的能力的,哪种 JavaScript Library 都没所谓的。
<br/>
<br/>我这里用jQuery把上面的这些M,V整合起来:
<br/><pre escaped=“true” lang=“javascript” line=“1”>var Person = Backbone.Model.extent({
<br/> sayHello: function () {
<br/> 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, I
m ’ + Person.get(‘name’) + ‘.’);
<br/> });
<br/> },
<br/> sayName: function () {
<br/> this.each(function (Person) {
<br/> $(‘body’).append('My name is ’ + Person.get(‘name’) + ‘.’);
<br/> });
<br/> }
<br/> });
<br/>var CNodejs = new Persons;
<br/>var persons,
<br/> tmpl-persons;
<br/>$.get(location.origin + ‘/persons’)
<br/> .done(function (data) {
<br/> persons = JSON.parse(data);
<br/> })
<br/> .get(location.origin + ‘/tmpls/persons.html’)
<br/> .done(function () {
<br/> tmpl-persons = JSON.parse(data);
<br/> });
<br/>CNodejs.add(persons.peoples);
<br/>$(‘body’).append(
<br/> Mustache.to_html(
<br/> tmpl-persons,
<br/> { peoples: CNodejs.toJSON() }
<br/> )
<br/>);</pre>
<br/>
<br/><hr />
<br/>
<br/><h1>Node.js MVC</h1>
<br/><h2>M - Model, V - View</h2>
<br/>其实 Backbone.js, Underscore.js 和 Mustache.js 在 Node.js 上的用法是和前端一模一样的,所以我就不多介绍了。
<br/><h2>C - Controller</h2>
<br/>来看看 Web.js 的 Router :
<br/><pre escaped=“true” lang=“javascript” line=“1”>var urlRouter = {
<br/> '^(\d{4})/(\d{2})/(\d{2})/(.).jpg’: ‘$1-$2-$3-$4.jpg’,
<br/> ‘google’: ‘http://www.google.com’,
<br/> ‘iwillwen’: ‘http://www.iwillwen.com’
<br/> },
<br/> getRouter = {
<br/> ‘^getsomthing’: function(req, res, qs) {
<br/> res.sendJSON(qs);
<br/> },
<br/> ‘^car’: function(req, res, qs) {
<br/> switch (qs.action) {
<br/> case ‘foo’:
<br/> res.send(‘Your action is foo’);
<br/> break;
<br/> case ‘bar’:
<br/> res.send(‘Your action is bar’);
<br/> break;
<br/> }
<br/> }
<br/> },
<br/> postRouter = {
<br/> ‘^postsomthing’: function(req, res, data) {
<br/> res.sendJSON(qs);
<br/> },
<br/> ‘^car’: function(req, res, data) {
<br/> switch (data.action) {
<br/> case ‘foo’:
<br/> res.send(‘Your action is foo’);
<br/> break;
<br/> case ‘bar’:
<br/> res.send(‘Your action is bar’);
<br/> break;
<br/> }
<br/> }
<br/> };
<br/>
<br/>web.run(urlRouter, 80)
<br/> .get(getRouter)
<br/> .post(postRouter);
<br/>
<br/>console.log(‘The app is running on http://localhost’);</pre>
<br/>这个和Express有点区别。
<br/>
<br/>如果结合数据库的 Node.js MVC,Web.js该怎么写呢
<br/><pre escaped=“true” lang=“javascript” line=“1”>var web =require(‘webjs’),
<br/> mongo = require(‘mongoskin’),
<br/> Backbone = require(‘backbone’),
<br/> Underscore = require(‘underscore’),
<br/> eventproxy = require(‘EventProxy.js’),
<br/> db = mongo.db(‘localhost:27017/blog’),
<br/> posts = db.collection(‘posts’),
<br/> metas = db.collection(‘metas’),
<br/>
<br/>var urlRouter = {
<br/> ‘^page/(\d)’: ‘page.html’,
<br/> ‘^(.)’: ‘post.html’
<br/> },
<br/> getRouter = {
<br/> ‘init’: function (req, res, qs) {
<br/> var proxy = new eventproxy.EventProxy(),
<br/> init = function (title, description, posts) {
<br/> var obj = {
<br/> title: title,
<br/> description: description,
<br/> posts: posts
<br/> };
<br/> res.sendJSON(obj);
<br/> };
<br/> proxy.assign(‘title’, ‘description’, ‘posts’, init);
<br/> metas.findOne({type: ‘title’}, function (err, title) {
<br/> proxy.trigger(‘title’, title);
<br/> });
<br/> metas.findOne({type: ‘description’}, function (err, description) {
<br/> proxy.trigger(‘description’, description);
<br/> });
<br/> posts.findTop10(function (err, posts) {
<br/> proxy.trigger(‘posts’, posts);
<br/> });
<br/> },
<br/> ‘getPost’: function (req, res, qs) {
<br/> posts.findOne(qs, function (err, post) {
<br/> res.sendJSON(post);
<br/> });
<br/> }
<br/> },
<br/> postRouter = {
<br/> ‘setMeta’: function (req, res, data) {
<br/> metas.update(
<br/> {type: data.type},
<br/> data,
<br/> {upsert: true},
<br/> function (err) {
<br/> if (err) return res.send(‘Set failed.
<br/>’ + err);
<br/> res.send(‘Set successed.’);
<br/> }
<br/> );
<br/> },
<br/> ‘post’: function (req, res, data) {
<br/> posts.update(
<br/> {title: data.title},
<br/> data,
<br/> {upsert: true},
<br/> function (err) {
<br/> if (err) return res.send(‘Post failed.
<br/>’ + err);
<br/> res.send(‘Post successed’);
<br/> }
<br/> );
<br/> }
<br/> };
<br/>web.run(urlRouter, 80)
<br/> .get(getRouter)
<br/> .post(postRouter);</pre>
<br/>
<br/><hr />
<br/>
<br/>最后来讲讲
<br/><h1>Web.js between client and server</h1>
<br/>来看看 Web.js for client (仍在编写中) 的用法
<br/><pre escaped=“true” lang=“javascript” line=“1”>(function ($) {
<br/> //loaded /js/web-client.js
<br/> var getRouter = {
<br/> ‘getsomething’ : function () {
<br/> var proxy = new Eventproxy(),
<br/> post = function (data, tmpl) {
<br/> $(‘body’).append(
<br/> Mustache.to_html(tmpl, data)
<br/> );
<br/> };
<br/> proxy.assign(‘data’, ‘tmpl’, init);
<br/> web.getData(‘post’, {}, function (data) {
<br/> proxy.trigger(‘data’, data);
<br/> });
<br/> web.getTmpl(‘post’, function (tmpl) {
<br/> proxy.trigger(‘tmpl’, tmpl);
<br/> });
<br/> }
<br/> };
<br/> web.conn(location.origin)
<br/> .get(getRouter);
<br/>})(jQuery);</pre>
<br/>
<br/><hr />
<br/>
<br/>这时候大家可能会问了,Web.js 的 MVC 究竟异样在哪里呢?
<br/>
<br/>前端 MVC 的 M 其实和后端 MVC 的 M 本来是不可能一起使用的,因为如果由后端进行模板渲染,前端就没有渲染的必要,那么前端 MVC就不成立了。同理,如果让前端进行模板渲染,后端也就不存在 MVC 的概念。
<br/>
<br/>如果要前后端的 MVC 同时存在要怎么做呢,其实很简单,就是让客户端进行性能·评估。
<br/>
<br/>比如说 IE6、7 这样的低性能浏览器,Web.js for client 会让服务器先进行 Data to HTML 渲染,然后传输再到客户端,如果是Chrome,FireFox,Safari 和 Opera 等高性能浏览器,则选择在客户端进行渲染,减轻传输荷载。
<br/>
<br/>另外 Web.js 默认会开启缓存应用加速机制 (DOM Storage,Cookies,Buffer,Object……),让一部分数据先存入缓存,让短时间内再次发出的请求从缓存中获取。减少 LAN 资源和数据库资源的损耗。
<br/>
<br/>JavaScript 是一门建立在静态页面上的动态脚本语言,Ajax的普及和发展,使它完全可以完成一些静态页面做不到的事情,比如像PHP自身的文件响应机制 (相比这也是 PHP 吸引人的一个终于优点)。
<br/>
<br/>上面的这一段代码就演示了一个静态页面通过 Web.js 进行 URL action router 识别,并向服务器请求数据和模板,然后在 DOM 中插入渲染得到的 HTML。
<br/>
<br/><img src=“http://i.minus.com/ijzKHS.jpg” alt="" />
<br/>
<br/>两个 Web.js 之间是可以无缝对接的,开发者无须设置太多。
<br/>
<br/>当然 Web.js for client 也是支持 Express 等其他 Server-side 开发框架的。
<br/>
<br/><hr />
<br/>
<br/>好,最后来放一个完整的 Web.js MVC Router 代码
<br/><pre escaped=“true” lang=“javascript” line=“1”>//server.js
<br/>var web =require(‘webjs’),
<br/> mongo = require(‘mongoskin’),
<br/> Backbone = require(‘backbone’),
<br/> Underscore = require(‘underscore’),
<br/> eventproxy = require(‘EventProxy.js’),
<br/> db = mongo.db(‘localhost:27017/blog’),
<br/> posts = db.collection(‘posts’),
<br/> metas = db.collection(‘metas’),
<br/>
<br/>var urlRouter = {
<br/> '^(.)’: ‘page.html’
<br/> },
<br/> getRouter = {
<br/> ‘init’: function (req, res, qs) {
<br/> var proxy = new eventproxy.EventProxy(),
<br/> init = function (title, description, posts) {
<br/> var obj = {
<br/> title: title,
<br/> description: description,
<br/> posts: posts
<br/> };
<br/> if (qs.render) {
<br/> res.send(
<br/> web.render(‘init’, obj)
<br/> );
<br/> } else {
<br/> res.sendJSON(obj);
<br/> }
<br/> };
<br/> proxy.assign(‘title’, ‘description’, ‘posts’, init);
<br/> metas.findOne({type: ‘title’}, function (err, title) {
<br/> proxy.trigger(‘title’, title);
<br/> });
<br/> metas.findOne({type: ‘description’}, function (err, description) {
<br/> proxy.trigger(‘description’, description);
<br/> });
<br/> posts.findTop10(function (err, posts) {
<br/> proxy.trigger(‘posts’, posts);
<br/> });
<br/> },
<br/> ‘getpost’: function (req, res, qs) {
<br/> posts.findOne(qs ,function (err, post) {
<br/> if (qs.render) {
<br/> res.send(
<br/> web.render(‘post’, post)
<br/> );
<br/> } else {
<br/> res.sendJSON(post);
<br/> }
<br/> });
<br/> }
<br/> };
<br/>web.run(urlRouter, 80)
<br/> .get(getRouter)
<br/> .set(‘tmplDir’, ‘tmpls’);
<br/>
<br/>//page.html - client.js
<br/>(function ($) {
<br/> var getRouter = {
<br/> ‘/’ : function () { //init
<br/> var proxy = new Eventproxy(),
<br/> init = function (data, tmpl) {
<br/> $(‘body’).append(
<br/> Mustache.to_html(tmpl, data)
<br/> );
<br/> };
<br/> proxy.assign(‘data’, ‘tmpl’, init);
<br/> web.getData(‘init’, {}, function (data) {
<br/> proxy.trigger(‘data’, data);
<br/> });
<br/> web.getTmpl(‘init’, function (tmpl) {
<br/> proxy.trigger(‘tmpl’, tmpl);
<br/> });
<br/> }
<br/> '^(.)’ : function (action) { // action --> (.)
<br/> var proxy = new Eventproxy(),
<br/> post = function (data, tmpl) {
<br/> $(‘body’).append(
<br/> Mustache.to_html(tmpl, data)
<br/> );
<br/> };
<br/> proxy.assign(‘data’, ‘tmpl’, init);
<br/> web.getData(‘post’, {title: action}, function (data) {
<br/> proxy.trigger(‘data’, data);
<br/> });
<br/> web.getTmpl(‘post’, function (tmpl) {
<br/> proxy.trigger(‘tmpl’, tmpl);
<br/> });
<br/> }
<br/> };
<br/> web.conn(location.origin)
<br/> .get(getRouter)
<br/>})(jQuery);</pre>
恭喜小问~~~快请客!
不错,小问 <br/>把一些优秀的框架也做了简介,学习了!
看到这样的代码: <br/> <br/>metas.findOne({type: ‘title’}, function (err, title) { <br/> proxy.trigger(‘title’, title); <br/>}); <br/>metas.findOne({type: ‘description’}, function (err, description) { <br/> proxy.trigger(‘description’, description); <br/>}); <br/>posts.findTop10(function (err, posts) { <br/> proxy.trigger(‘posts’, posts); <br/>}); <br/> <br/>我觉得没用Jscex真是可惜啊: <br/> <br/>var title = $await(metas.findOneAsync({type: “title”})); <br/>var description = $await(metas.findOneAsync({type: ‘description’})); <br/>var posts = $await(metas.findOneAsync()); <br/> <br/>现在文章里的代码,一点错误处理都没有的,按理来说应该到处是 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 形式。我省略了包导入的部分。 […]
林子好大