[实践经验+代码]用node.js和express.js和jade搭建轻型cms系统

标签: ppt 代码 资料 cms express | 发表时间:2011-08-10 13:04 | 作者:胡天硕-tianshuo zhibin
出处:http://cnodejs.org/blog

用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.jsnodejs.org)运行快开发快的服务器框架,使用v8跑javascript
  • express.jsexpressjs.com) node.js上目前最好的网站服务器框架,尤其特别合适做REST协议。
  • jadejade-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源码研究—模块组织加载
无觅

相关 [实践 经验 代码] 推荐:

同行代码评审过程中的实践经验

- - 博客 - 伯乐在线
数百万年前,猿从树上下来,进化出了对生拇指,最终,变成了人类. 我们以类似的眼光来看下强制性代码评审(Code Review):好像是一种能在软件开发这块广阔的领域里将人类从兽里分离出来的东西. 不过,我有时候会从我们的团队成员里听到下面这样的评论:. “这个项目的代码评审根本就是浪费时间. “我的项目发布延期了,都是因为我那懦弱的同事还没有做任何评审.

[实践经验+代码]用node.js和express.js和jade搭建轻型cms系统

- zhibin - CNode社区
用node.js+express.js轻型CMS系统第一讲. 我们主要做的是iphone/ipad程序,但关注node.js很久,因为我们多少总是要做网站,做后台. node.js就像一个非常快的ruby. 对于我们而言,其实学习node.js起来还是很简单,网上资料很多,但没有看到一些比较完整的例子.

[译] 代码审查之最佳实践

- - IT瘾-dev
原文:https://medium.com/palantir/code-review-best-practices-19e02780015f 作者:Robert F. 图片来自 https://xkcd.com/1513/. 代码审查之为什么、查什么、何时查. 之所以要执行代码审查(code reviews),就是为了籍此改善代码质量,并向团队和公司文化中注入正能量.

最佳实践系列:前端代码标准和最佳实践

- - 博客园_旁观者
最佳实践系列:前端代码标准. @窝窝商城前端(刘轶/李晨/徐利/穆尚)翻译于2012年 版本0.55 @郑昀校对. isobar的这个前端代码标准和最佳实践文档,涵盖了Web应用开发的方方面面,我们翻译了大部分章节,并做了注解. 渐进增强; Combo Handler; Quirks Mode; 浏览器盒子模型; 选择器特殊性; Spacer Image; CSS Sprites; PNG8;.

移动应用可用性测试的实践经验总结

- - 互联网的一些事-关注互联网产品管理,交流产品设计、用户体验心得
  如果你不大熟悉移动应用的可用性测试,没关系,这事儿没你想象的那么困难;不过移动应用与传统网站产品在可用性测试方面确实有一些关键的区别需要我们注意.   过去的几年当中,我(英文原文作者)为不少移动产品做过测试,从戒烟应用到移动版的车辆保险网站,其中既包括在实验室使用复杂设备进行的测试,也包括在各种实境化的条件下进行的非正式测试.

可伸缩性最佳实践:来自eBay的经验

- - 人月神话的BLOG
原文: http://www.infoq.com/cn/articles/ebay-scalability-best-practices. 在eBay,可伸缩性是我们每天奋力抵抗的一大架构压力. 我们所做的每一项架构及设计决策,身前身后都能看到它的踪影. 当我们面对的是全世界数以亿计的用户,每天的页面浏览量超过10亿,系统中的数据量要用皮字节(1015或250)来计算——可伸缩性是生死交关的问题.

异地多活(异地双活)实践经验 - CSDN博客

- -
异地多活(异地双活)是最近业界讨论比较多的话题,特别是前一阵子支付宝机房光纤故障和携程网数据库丢失之后,更加唤起了技术人员们对异地容灾的考虑. 而异地多活比异地容灾更高一级,因为异地容灾仅仅是一个冷备的概念,而异地多活却是指有两个或者多个可以同时对外服务的节点,任意一个点挂了,也可以迅速切换到其他节点对外服务,节点之间的数据做到准实时同步.

高效代码审查的十个经验

- - 月光博客
  代码审查(Code Review)是软件开发中常用的手段,和QA测试相比,它更容易发现和架构以及时序相关等较难发现的问题,还可以帮助团队成员提高编程技能,统一编程风格等.   团队需要认识到代码审查是为了提高整个团队的能力,而不是针对个体设置的检查“关卡”.   “A的代码有个bug被B发现,所以A能力不行,B能力更好”,这一类的陷阱很容易被扩散从而影响团队内部的协作,因此需要避免.

写好Java代码的30条经验总结

- - Java - 编程语言 - ITeye博客
成为一个优秀的Java程序员,有着良好的代码编写习惯是必不可少的. 下面就让我们来看看代码编写的30条建议吧. (1) 类名首字母应该大写. 字段、方法以及对象(句柄)的首字母应小写. 对于所有标识符,其中包含的所有单词都应紧靠在一起,而且大写中间单词的首字母. 若在定义中出现了常数初始化字符,则大写static final基本类型标识符中的所有字母.

前端代码标准最佳实践:CSS

- - CSDN博客推荐文章
上一篇《 前端代码标准最佳实践:javascript》发表后,大家讨论还是很热烈,从侧面体现了前端工程师对写标准的前端代码的重视程度很高. 这些最佳标准实践并不是那个权威组织发布的,而是由大量的前端工程师们在实践过程中的经验总结,目的在于提高代码的可读性,可维护性和性能. 那么接着上一篇,我们再来谈谈CSS代码的一些标准实践.