/ Node.js

用 Express 4.0 重写《Node.js 开发指南》第5章的项目实例

首先,这篇文章的存在告诉我们,对于新兴的技术,不要买纸质书

《Node.js 开发指南》
应该是国内最早的 Node.js 教程,这本书由浅入深地介绍了 Node.js 的一些用法,尤其是包管理的部分非常实用。但是由于年代久远(2012年7月),版本更迭,书中第5章的实例已经不能直接使用,尤其是涉及 Express 框架的部分,所以看书的同时试着用新版 Express 来完成书中的例子。

Express 4.0 的默认模板引擎是 jadejade 的优点是代码简洁、表达能力强,但语法与 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

初始的项目文件结构大概是这样的:

Express 项目初始文结构

可以看到,项目文件结构与书中的相比已经发生了一些变化,例如将启动端口监听的代码放在了 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>

页面效果:

index.ejs 页面效果

参考