[实践经验+代码]用node.js和express.js和jade搭建轻型cms系统
用node.js+express.js轻型CMS系统第一讲
先看看效果图
前言:
我们主要做的是iphone/ipad程序,但关注node.js很久,因为我们多少总是要做网站,做后台。node.js就像一个非常快的ruby。对于我们而言,其实学习node.js起来还是很简单,网上资料很多,但没有看到一些比较完整的例子。所以回报一下大家,在这里把我们基本上最核心的源码分享给大家了。
背景介绍:
我用node.js已经实现过一些小的应用利用websockets配合移动终端做网页同步显示,但是没有一个真正的项目。
最近把公司官网移植到node.js上,已经上线:qinyh.com。
我们官网内容大概很少会变化,于是我们考虑把内容放在一个js文件里(chinese.json),而所有的路径放在另一个js文件里(route.json)。总共最后用了包括layout和404页面在内的八个视图。虽然不能说是一个完整的cms,但是已经实现了版面与内容的分离。也可以非常轻松地修改内容和图片。
我们还用ajax配合node-mailer实现异步的发信功能,这个以后跟大家介绍。
核心技术介绍:
- node.js (nodejs.org)运行快开发快的服务器框架,使用v8跑javascript
- express.js(expressjs.com) node.js上目前最好的网站服务器框架,尤其特别合适做REST协议。
- jade (jade-lang.com)一个非常干净易用的html模板语言
框架结构
- 代码
- /server.js 80端口上的服务器(负责虚拟主机)
- /app.js 官网服务器
- /email.js 邮件模块(这次没有讲)
- 路由列表
- /router.json 关联路径,视图和内容
- 视图
- /views/layout.jade 基础模板
- /views/index.jade 首页
- /views/…. 还有6个模板
- /views/404.jade 错误页面
- 内容
- /chinese.json 存放内容和图片路径
- 其他静态资源
- /public/js/… 存放js文件
- /public/css/… 存放css文件
- /public/images/… 存放图片
联系我们
有什么疑问可以直接留言,或者也可以联系我:hts_某种符号_qinyh.com (也欢迎node.js工程师投简历)
接下来是代码
/server.js
我们使用forever(https://github.com/indexzero/forever)来跑node.js脚本,这样可以保证服务器不会down掉。
由于我们主机还想做别的项目所以我们要用到虚拟主机:
/** * Module dependencies. */ var express = require('express'); var offical = require('./app.js'); var site_vhosts=[],vhosts; // Virtual Hosts site_vhosts.push(express.vhost('qinyh.com',offical)); site_vhosts.push(express.vhost('www.qinyh.com',offical)); vhost=express.createServer.apply(this,site_vhosts); vhost.listen(80); console.log("Express router Listening on port 80");
/app.js
以下才是我们程序的主干部分,忽略掉了ajax邮件发送模块⋯⋯下次再讲
注意只有当在production mode时,才会开启缓存,才保证性能。
/** * Module dependencies. */ var express = require('express'); var app = module.exports = express.createServer(); var fs=require('fs'); // Configuration var oldconsole=console.log; log=function(obj,error){ if(process.platform!="win32"){ var color=(error)?"33[1;31m":"33[1;32m"; process.stdout.write(color); oldconsole(obj); process.stdout.write("33[0m"); }else{ oldconsole(obj); } } console.log=function(obj){ log(obj,true); } app.configure(function(){ app.set('views', __dirname + '/views'); app.set('view engine', 'jade'); app.use(express.bodyParser()); app.use(express.methodOverride()); app.use(express.static(__dirname + '/public')); //注意顺序,为了能够用到404,要把这个提前。 app.use(app.router); }); app.configure('development', function(){ app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); log("Warning: Server in Development Mode, add NODE_ENV=production",true); }); app.configure('production', function(){ app.use(express.errorHandler()); log("Production Mode"); }); // Read JSON files //这里出现过一个非常恶心的bug,我们发现我们拿windows记事本产生的json文件node.js解析会有问题,于是去掉第一个字节。为了保证安全,文件也上来加了一个回车。 //由于我们是一上来只解析一次,所以我们采用了同步方式
var info=JSON.parse(fs.readFileSync('chinese.json', 'utf8').substr(1)); var routes=JSON.parse(fs.readFileSync('router.json','utf8').substr(1)); // Start router var startRouter=function(path){ app.get(route, function(req,res){ //console.log("Connect to "+path); var page=info[routes[path].data]; res.render(routes[path].template,page);//最核心的一句 }); }; for(route in routes){//如果直接for循环而不是调用函数,你就会发现route永远是最后一个 startRouter(route); } //File not found app.get('/*', function(req, res){ res.render('404',{status: 404, title:'404 - 文件未找到'}); }); try{ app.listen(3000); log("Express server listening on port 3000"); }catch(e){ log("Error: "+e.message,1); }
/router.json
我们通过router.json实现了路由表,可以看到index是view,而data是chinese.json里对应的内容:
{ "/": { "template": "index", "data": "index" }, "/products": { "template": "secondary", "data": "products" }, "/products/clothes": { "template": "products", "data": "clothes" }, "/products/wine": { "template": "products", "data": "wine" }, "/products/furniture": { "template": "products", "data": "furniture" }, "/solutions": { "template": "secondary", "data": "solutions" }, "/solutions/shop": { "template": "solutions", "data": "shop" }, "/solutions/e-shop": { "template": "solutions", "data": "eshop" }, "/solutions/next-gen": { "template": "solutions", "data": "nextgen" }, "/company": { "template": "secondary", "data": "company" }, "/company/team": { "template": "about", "data": "team" }, "/company/ideals": { "template": "company", "data": "ideals" }, "/company/contact": { "template": "contact", "data": "contact" }, "/company/jobs": { "template": "secondary", "data": "jobs" }, "/company/jobs/it": { "template": "jobs", "data": "it" }, "/company/jobs/design": { "template": "jobs", "data": "design" }, "/company/jobs/sales": { "template": "jobs", "data": "sales" } }
/chinese.json
然后内容和图片是放在chinese.json里,我们由于一般改动很少,所以直接文本编辑器就很方便。如果经常更新,其实加一个后台也非常容易。
实际内容太多,只给两个例子
{ "index": { "title": "首页 | 北京秦运恒信息技术有限公司", "motto": "原来购物可以如此简单和生动", "columns": [ { "title": "最新产品", "desc": "使用iPad展示商品为顾客提供更加丰富的渠道来探索和喜爱您的产品。", "img": "/images/index/img1.png", "href": "/products/" }, { "title": "解决方案", "desc": "我们会与您一起找到适合您的解决方案,让您的事业蒸蒸日上。", "img": "/images/index/img2.png", "href": "/solutions/" }, { "title": "关于我们", "desc": "年轻而专业。梦想加实干。敏捷和执着。这就是我们,您一定会喜欢跟我们合作。", "img": "/images/index/img3.png", "href": "/company/" } ] }, "products": { "title": "产品介绍 | 北京秦运恒信息技术有限公司", "motto": "精益求精,宁缺毋滥", "banner": "banner-2", "columns": [ { "title": "精品家具专家", "desc": "苹果iPad上展现家具的全部风采,捕捉顾客的想象力和心,连样板间都可以展现上百种家具。", "img": "/images/product/img1.png", "href": "/products/furniture/" }, { "title": "葡萄酒指南", "desc": "中国即将成为世界红酒消费第一大国,然而多数消费者只认得电视广告。有了这样的指南,您的好酒再也不愁无人问津。", "img": "/images/product/img2.png", "href": "/products/wine/" }, { "title": "服装时尚导购", "desc": "顾客看到苹果iPad上的服装模特会动的那一刻,已经决定了您和其他店的区别。从今以后您的顾客可以和朋友一起坐着逛街了。", "img": "/images/product/img3.png", "href": "/products/clothes/" } ] }, .... ]
/views/layout.jade
然后就是通用的模板layout.jade,放在views目录下,用jade写的,非常简约。!=body 是插入别的页面的地方。
!!! transitional html(xmlns='http://www.w3.org/1999/xhtml') head meta(http-equiv='Content-Type', content='text/html; charset=utf-8') title= title link(rel='shortcut icon', href='/favicon.ico', type='image/x-icon') link(rel='stylesheet', type='text/css', href='/css/index.css') body #all div(style='clear:both') #logo.content a(href='/') img(src='/images/logo.png', width='232', height='35', alt='北京秦运恒信息技术有限公司') ul li a(href='/') 首页 li a(href='/products/') 产品介绍 li a(href='/solutions/') 解决方案 li a(href='/company/') 关于我们 li a(href='/company/jobs/') 加入我们 !=body #footer p(align='center') @2010 北京秦运恒信息技术有限公司 京备ICP10028133号
/views/index.jade
有那么多视图就不上传了,我就把首页上传给大家看看
#p-1.content p= motto #main.content - for (var i in columns) - var last=(i<columns.length-1)?'':'last' dl(class='#{last}') dt a(href='#{columns[i].href}') img(src='#{columns[i].img}', width='276', height='164', alt='#{columns[i].title}') dd a(href='#{columns[i].href}') span=columns[i].title br | #{columns[i].desc} .slider-wrapper.theme-custom .slider-wrapper2 #slider.nivoSlider img(src='../images/index/banner1.png', width='100%', height='366', alt='首页') img(src='../images/index/banner2.png', width='100%', height='366', alt='首页') img(src='../images/index/banner3.png', width='100%', height='366', alt='首页') script(type='text/javascript', src='/js/jquery-1.6.1.min.js') script(type='text/javascript', src='/js/jquery.nivo.slider.js') script(type='text/javascript') $(document).bind("ready",function() { $('#slider').nivoSlider(); });
其他资源
其他资源一股脑扔倒/public目录下就可以了。
请关注第二讲,AJAX和邮件系统
您可能也喜欢: | ||||
Web.js MVC between client and server |
几个常用字符串hash算法的node封装 |
Node 下 Http Streaming 的跨浏览器实现 |
nodejs快速入门 |
node.js源码研究—模块组织加载 |
无觅 |