用 Express 4.0 重写《Node.js 开发指南》第5章的项目实例
首先,这篇文章的存在告诉我们,对于新兴的技术,不要买纸质书。
《Node.js 开发指南》
应该是国内最早的 Node.js 教程,这本书由浅入深地介绍了 Node.js 的一些用法,尤其是包管理的部分非常实用。但是由于年代久远(2012年7月),版本更迭,书中第5章的实例已经不能直接使用,尤其是涉及 Express 框架的部分,所以看书的同时试着用新版 Express 来完成书中的例子。
Express 4.0 的默认模板引擎是 jade
,jade
的优点是代码简洁、表达能力强,但语法与 HTML 差别很大,需要额外的学习成本。为了方便起见,这里将模板引擎改成 ejs
(Embedded JavaScript),并且尽量用原生的 Express(不安装非官方的包)来完成书中的例子。如果想挑战高难度的话,也可以尝试着用 jade
完成书中的例子。
项目初始化
目前用 npm 安装 Express 后不能直接使用 express 命令(使用了 -g
参数也不行),因为 Express 将快速搭建功能剥离成了一个单独的工具 Express(1),对应的包名称是 express-generator
:
npm install -g express
npm install -g express-generator
另外,建立工程的命令也有变化,原先的 -t 参数已经不再使用,可以使用 -e 参数来使用 ejs 引擎:
express -e microblog
初始的项目文件结构大概是这样的:
可以看到,项目文件结构与书中的相比已经发生了一些变化,例如将启动端口监听的代码放在了 bin/www
中。另外,涉及路由控制的代码也有细微的改动,不过新旧代码之间的对应关系很容易确定。
另外,模板目录下不再生成 layout.ejs
文件。根据这个帖子,从 Express 3.x 开始,支持 layout 的任务交给了模板引擎,想要使用 layout 文件,可以使用 ejs-locals 或者 express-partials。
项目启动
完成之后,还需要切换到新生成的项目根目录下,使用 npm-install
在项目本地安装需要的包。之后就可以使用 npm start
来启动服务器,或者用 node bin/www
。按书中写的 node app.js
已经不起作用,原因是项目的文件结构也发生了变化,启动监听服务的代码被移到了 bin/www
脚本中。
cd microblog
npm install
npm start 或 node bin/www
之所以能用 npm start
来启动服务,是因为项目的 package.json 文件中定义了如下的属性:
{
// 省略的代码
\"scripts\": {
\"start\": \"node bin/www\"
},
// 省略的代码
}
可以看出,运行 npm start
的作用是让 npm 帮我们运行 node bin/www
这个命令。
运行 npm start 后,shell 中并没有给出端口号等提示信息。
可以在 bin/www 加上 console.log() 来输出正在监听的端口号:
var server = app.listen(app.get('port'), function() {
debug('Express server listening on port ' + server.address().port);
console.log('Express server listening on port ' + server.address().port);
});
为 index 创建路由规则
在 ./routes/index.js
中添加
router.get('/hello', function(req, res) {
res.render('index', { title: \"Hello!\" });
});
访问 localhost:3000/hello 尝试刚刚添加的路由规则。
URL路由规则
打开 app.js,可以看到创建路由的代码也有所变化。Express 已经为我们创建了 '/'、'/users'这两个路由。代码中首先引入了路由脚本,然后再通过 app 变量添加:
var routes = require('/routes/index');
// ...
app.use('/', routes);
因此可以按照书中的路由规划,添加 /u/:user、post 等路由规则:
// 新的路由规则变量
var user = require('./routes/u');
var post = require('./routes/post');
var reg = require('./routes/reg');
var login = require('./routes/login');
var logout = require('./routes/logout');
// 新的路由规则
app.use('/u/:user', user);
app.use('/post', post);
app.use('/reg', reg);
app.use('/login', login);
app.use('logout', logout);
编写 ejs 模板
由于 Bootstrap 3 的样式类也发生了一些改变,书中的 html 代码也需要作一些修改。另外,可以直接在模板文件中插入类似导航栏与页脚这样的小模块,不需要再用 partials(partials 默认也是不安装的)。
header.ejs
<!DOCTYPE html>
<html>
<head>
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">
<title><%= title %></title>
<link rel='stylesheet' href='/stylesheets/style.css' />
<link rel='stylesheet' href='/bootstrap/css/bootstrap.css' />
</head>
<body>
<header>
<div class=\"navbar navbar-inverse navbar-fixed-top\" role=\"navigation\">
<div class=\"container\">
<div class=\"navbar-header\">
<a href=\"/\" class=\"navbar-brand pull-left\">MicroBlog</a>
<button class=\"navbar-toggle\" data-toggle=\"collpase\" data-target=\".navbar-collapse\" type=\"button\">
<span class=\"glyphicon glyphicon-align-justify\"></span>
</button>
</div>
<nav class=\"navbar-collapse collapse\">
<ul class=\"navbar-nav nav\">
<li class=\"active\"><a href=\"/\">首页</a></li>
<li><a href=\"login\">登录</a></li>
<li><a href=\"reg\">注册</a></li>
</ul>
</nav>
</div>
</div>
</header>
index.ejs
<%- include header %>
<div class=\"banner jumbotron\">
<div class=\"container\">
<h1>MicroBlog</h1>
<p>基于 Node.js 的试验站点</p>
<a class=\"btn btn-primary btn-large\" href=\"/login\">登录</a>
<a class=\"btn btn-large\" href=\"/reg\">立即注册</a>
</div>
</div>
<div class=\"row\">
<div class=\"container\">
<div class=\"col-md-4 content\">
<h2>Carbo 说</h2>
<p>Node.js 好(啊),欧巴都说**(啊)!</p>
</div>
<div class=\"col-md-4 content\">
<h2>BYVoid 说</h2>
<p>OpenCC是一个开源的中简繁转换项目,致力于制作高质量的基于统计语料的简繁转换词库。</p>
</div>
<div class=\"col-md-4 content\">
<h2>虎扑足球 说</h2>
<p>【镜报:曼城有意免签切尔西老将阿什利-科尔】曼城令人意外地看中了切尔西后卫阿什利-科尔。阿什利-科尔至今仍未与切尔西就续约达成一致,有消息称,他可能在本赛季结束后离开斯坦福桥。蓝军只愿意为这名33岁老将送上一份为期一年的新合同,但他却并不缺少追求者。http://t.cn/8seovTk</p>
</div>
</div>
</div>
<%- include footer %>
<script src=\"/javascripts/jquery-1.11.0.js\"></script>
<script src='/bootstrap/js/bootstrap.js'></script>
</body>
</html>
footer.ejs
<footer>
<div class=\"container\">
<div class=\"col-md-4\">
<p><a href=\"http://qianyanseu.tk\" target=\"_blank\">Y. Qian</a> 2014</p>
</div>
</div>
</footer>
页面效果: