[实践经验+代码]用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源码研究—模块组织加载 |
| 无觅 | ||||




