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

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, Im 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, Im ’ + 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>

9 回复

恭喜小问~~~快请客!

不错,小问 <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 形式。我省略了包导入的部分。 […]

回到顶部