Express 教程的第二篇文章,演示如何创建一个 "骨架" 网站项目,你可以接着在里面加入网站特定的路由、模板/视图、和数据库调用。

前置条件: 配置一个Node 开发环境。回顾Express 教程。
目标: 能够使用Express 应用产生器,创建自己新的网页项目。

概览

本文演示如何使用 Express 应用产生器 工具,创建一个 "骨架" 网站,然后您可以使用特定于站点的路由,视图/模板和数据库调用来填充它们。在这个教程,我们将使用该工具,为我们的本地图书馆网站创建框架,我们稍后将添加该网站所需的所有其他代码。该过程非常简单,只需要在命令行上,用新项目名称调用生成器,还可以指定站点的模板引擎和CSS生成器。

以下部分向您展示如何调用应用程序生成器,并提供关于视图/ CSS的不同选项的一些解释。我们还将解释骨架网站的结构。最后,我们将展示如何运行网站,来验证它是否有效。

注意:Express Application Generator并非Express应用程序的唯一生成器,生成的项目不是构建文件和目录的唯一可行方式。然而,生成的网站具有易于扩展和理解的模块化结构。有关最小Express应用程序的信息,请参阅Hello world示例(Express docs)。

使用应用产生器

您应该已经安装了生成器,作为设置Node开发环境的一部分。作为快速提醒,您可以使用NPM软件包管理器,在整个站点安装生成器工具,如下所示:

npm install express-generator -g

生成器有许多选项,您可以使用--help(或-h)命令,在命令行上查看它们:

> express --help

  Usage: express [options] [dir]

  Options:

    -h, --help           output usage information
        --version        output the version number
    -e, --ejs            add ejs engine support
        --pug            add pug engine support
        --hbs            add handlebars engine support
    -H, --hogan          add hogan.js engine support
    -v, --view <engine>  add view <engine> support (ejs|hbs|hjs|jade|pug|twig|vash) (defaults to jade)
    -c, --css <engine>   add stylesheet <engine> support (less|stylus|compass|sass) (defaults to plain css)
        --git            add .gitignore
    -f, --force          force on non-empty directory

您可以使用 Jade 视图引擎和纯 CSS 來指定express,以在当前目录中创建项目(如果指定目录名,则项目将创建在具有该名称的子文件夹中)。

express

您还可以使用--view选择视图(模板)引擎,并且/或者使用--css选择 CSS 生成引擎。

Note: The other options for choosing template engines (e.g. --hogan, --ejs, --hbs etc.) are deprecated. Use --view (or -v)!

我应该用哪个视图引擎?

Express Application Generator允许您配置许多流行的视图/模板引擎,包括 EJSHbsPug (Jade)、TwigVash,但如果您没有指定视图选项,它会默认选择Jade。Express 本身也可以支持大量其他模板语言,是「开箱即可使用」的。

注意: 如果要使用生成器不支持的模板引擎,请参阅在Express中使用模板引擎(Express文档),并参阅目标视图引擎的文档。

一般来说,您应该选择一种「可以提供您所需的所有功能」的模板引擎,
并使您能够尽早提高生产力 - 换句话说,就像您选择其他组件一样!比较模板引擎时需要考虑的一些事项如下:

  • 具备生产力之前所需要花费的时间 — 如果您的团队已经有模板语言的经验,那么使用该语言可能会更快地提高生产力。如果不是,那么你应该考虑候选模板引擎的相对学习曲线(即以投入时间与得到的生产力为XY座标所绘制的曲线)。
  • 人气和活跃 — 回顾引擎的普及程度以及它是否拥有活跃的社区。当您在网站的生命周期中遇到问题时,能够获得对引擎的支持非常重要。
  • 风格 — 某些模板引擎使用特定的标记,来指示在 “普通” HTML内插入的内容,而其他模板引擎使用不同的语法(例如,使用缩进和区块名称)构造HTML。
  • 性能/渲染时间。
  • 特点 — 你应该考虑你看中的引擎是否具有以下功能:
    • 布局继承:允许您定义基本模板,然后 “继承” 它的一部分,使特定页面可以有不同的呈现。比起通过包含大量必需组件,或每次从头开始构建模板,这通常是更好的方式。
    • "Include" 支持:允许您通过包含其他模板,来构建模板。
    • 简洁的变量和循环控制语法。
    • 能够在模板级别过滤变量值(例如,将变量设置为大写,或格式化日期值)。
    • 能够生成HTML以外的输出格式(例如 JSON 或 XML)。
    • 支持异步操作和流媒体。
    • 可以在客户端和服务器上使用。如果可以在客户端上使用模板引擎,则允许提供数据,并有可能在客户端完成所有渲染,或大部分渲染。

提示: 互联网上有许多资源,可帮助您比较不同的视图/模板引擎选择!

对于这个项目,我们将使用 Pug 模板引擎(这是最近更名的Jade引擎),因为这是最流行的 Express / JavaScript 模板语言之一,并且应用发生器支持开箱即用

我應該用哪個CSS樣式引擎?

Express 应用生成器允许您创建一个项目,并配置最常见的 CSS 样式表引擎:LESS, SASS, Compass, Stylus

注意: CSS有一些限制,使某些任务变得困难。 CSS 样式表引擎允许您使用更强大的语法来定义您的 CSS,然后将定义编译为纯粹的旧式 CSS,以供浏览器使用。

与模板引擎一样,您应该使用样式表引擎,这样可以让您的团队获得最高生产力。对于这个项目,我们将使用普通的CSS(默认值),因为我们的CSS 要求不够复杂,没有必要使用其他任何东西。

我应该用哪个数据库?

生成的代码不使用/包含任何数据库。 Express 应用程序可以使用 Node支持的任何数据库机制(Express 本身并未针对数据库管理,定义任何特定的附加行为/要求)。

我们将在后面的文章中,讨论如何与数据库集成。

创建项目

对于我们要构建的示例 Local Library 应用程序,我们将使用 Pug 模板库,创建一个名为 express-locallibrary-tutorial 的项目,并且不使用 CSS样式表引擎。

首先到要创建项目的位置,然后在命令提示符下,运行 Express 应用生成器,如下所示:

express express-locallibrary-tutorial --view=pug

生成器将创建(并列出)项目的文件。

   create : express-locallibrary-tutorial
   create : express-locallibrary-tutorial/package.json
   create : express-locallibrary-tutorial/app.js
   create : express-locallibrary-tutorial/public/images
   create : express-locallibrary-tutorial/public
   create : express-locallibrary-tutorial/public/stylesheets
   create : express-locallibrary-tutorial/public/stylesheets/style.css
   create : express-locallibrary-tutorial/public/javascripts
   create : express-locallibrary-tutorial/routes
   create : express-locallibrary-tutorial/routes/index.js
   create : express-locallibrary-tutorial/routes/users.js
   create : express-locallibrary-tutorial/views
   create : express-locallibrary-tutorial/views/index.pug
   create : express-locallibrary-tutorial/views/layout.pug
   create : express-locallibrary-tutorial/views/error.pug
   create : express-locallibrary-tutorial/bin
   create : express-locallibrary-tutorial/bin/www

   install dependencies:
     > cd express-locallibrary-tutorial && npm install

   run the app:
     > SET DEBUG=express-locallibrary-tutorial:* & npm start

在输出结束时,生成器提供关于「如何安装依赖关系」的指示信息(如package.json 文件中所列),以及如何运行应用程序(上述说明适用于Windows;在Linux / macOS上,它们会略有不同)。

运行骨架网站

在这一时间点上,我们有一个完整的骨架项目。该网站实际上并没有做太多工作,但运行它,能夠展示它是如何工作的。

  1. 首先安装依赖项(安装命令install,将获取项目的 package.json 文件中列出的所有依赖项包)。
    cd express-locallibrary-tutorial
    npm install
  2. 然后运行该应用程序。
    • 在Windows上,使用此命令:
      SET DEBUG=express-locallibrary-tutorial:* & npm start
    • 在macOS or Linux,使用此命令:
      DEBUG=express-locallibrary-tutorial:* npm start
      
  3. 然后在浏览器中加载 http://localhost:3000/ ,以访问该应用程序。

你应该会看到一个浏览器页面,就像这样:

Browser for default Express app generator website

你有一个能工作的 Express 应用了,让它在 http://localhost:3000/ 服务 。

注意: 您也可以使用npm start命令启动应用程序。如下图所示,指定 DEBUG 变量可启用控制台日志记录/调试。例如,当你访问上面的页面时,你会看到像这样的调试输出:

>SET DEBUG=express-locallibrary-tutorial:* & npm start

> express-locallibrary-tutorial@0.0.0 start D:\express-locallibrary-tutorial
> node ./bin/www

  express-locallibrary-tutorial:server Listening on port 3000 +0ms
GET / 200 288.474 ms - 170
GET /stylesheets/style.css 200 5.799 ms - 111
GET /favicon.ico 404 34.134 ms - 1335

让伺服器在档案更改时重新启动

在您重新启动服务器之前,您对 Express 网站所做的任何更改,目前都不可见。每次进行更改时,必须停止并重新启动服务器,很快变得非常烦人,因此值得花时间使服务器在需要时,自动重新启动。

这种工具中,最简单的之一就是 nodemon。这通常是全局安装的(因为它是一个“工具”),但在这里,我们将在本地安装和使用它,作为开发人员依赖项,以便任何使用该项目的开发人员,在安装应用程序时自动获取它。在骨架项目的根目录中,使用以下命令:

npm install --save-dev nodemon

如果您打开项目的 package.json 文件,您现在将看到一个具有此依赖关系的新区段:

  "devDependencies": {
    "nodemon": "^1.14.11"
  }

由于该工具没有全局安装,我们无法从命令行启动它(除非我们将其添加到路径中),但是我们可以从 NPM 脚本中调用它,因为 NPM 知道所有关于安装的软件包的信息。找到你的 package.json 的脚本 scripts 区块。我们更新scripts 区块,最初的一行,以"start"开头,在该行的末尾添加逗号,并添加 "devstart" 开头的一行,如下所示:

  "scripts": {
    "start": "node ./bin/www",
    "devstart": "nodemon ./bin/www"
  },

现在我们可以用与前面几乎完全相同的方式,启动服务器,但使用指定的 devstart 命令:

  • 在 Windows,使用此命令:
    SET DEBUG=express-locallibrary-tutorial:* & npm run devstart
  • 在 macOS or Linux,使用此命令:
    DEBUG=express-locallibrary-tutorial:* npm run devstart
    

注意: 现在,如果您编辑项目中的任何文件,服务器将重新启动(或者您可以随时在命令提示符下,键入rs来重新启动它)。您仍需要重新加载浏览器,以刷新页面。

我们现在必须调用“npm run <scriptname>”而不是 npm start,因为“start”实际上是映射到指定脚本的 NPM 命令。我们可以在启动脚本中替换该命令,但我们只想在开发期间使用 nodemon,因此创建新的脚本命令是有意义的。

从产生器得到的项目

现在我们来看看我们刚刚创建的项目。

目錄結構

从产生器得到的生成项目,现在已经安装了依赖项,具有以下文件结构 (带前缀 “/” 的项目,表示文件)。package.json 文件定义了应用程序依赖项,和其他信息。它还定义了一个启动脚本,它将调用应用程序入口点 JavaScript 文件 /bin/www。这设置了一些应用程序的错误处理,然后加载 app.js ,来完成剩下的工作。应用程序路径,存储在 /routes 目录下的单独模块中。模板存储在 /views 目录下。

/express-locallibrary-tutorial
    app.js
    /bin
        www
    package.json
    /node_modules
        [about 4,500 subdirectories and files]
    /public
        /images
        /javascripts
        /stylesheets
            style.css
    /routes
        index.js
        users.js
    /views
        error.pug
        index.pug
        layout.pug
    

以下各节将详细介绍这些文件。

package.json

package.json 文件定义了应用程序依赖关系,和其他信息:

{
  "name": "express-locallibrary-tutorial",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "start": "node ./bin/www",
    "devstart": "nodemon ./bin/www"
  },
  "dependencies": {
    "body-parser": "~1.18.2",
    "cookie-parser": "~1.4.3",
    "debug": "~2.6.9",
    "express": "~4.16.2",
    "morgan": "~1.9.0",
    "pug": "~2.0.0-rc.4",
    "serve-favicon": "~2.4.5"
  },
  "devDependencies": {
    "nodemon": "^1.14.11"
  }
}

依赖关系包括 express 包,和我们所选视图引擎(pug)的包。另外,我们还有以下的包,在许多 Web 应用程序中很有用:

  • body-parser:  这解析传入HTTP请求的正文 body 部分,并更容易提取包含信息的不同部分。例如,您可以使用它来读取 POST 参数。
  • cookie-parser:  用于解析 cookie header 并填充 req.cookies(本质上提供了访问 cookie 信息的便捷方法)。
  • debug: 一个小型 node 调试程序,仿照 node 核心的调试技术建立的。
  • morgan:  搭配 node 使用的 HTTP 请求记录器中间层软件。
  • serve-favicon:  用于提供收藏图标 favicon 的 node 中间层软件(这是用于表示浏览器选项卡、书签等网站内的图标)。

脚本部分,定义了一个“开始” "start" 脚本,当我们调用 npm start  来启动服务器时,这就是我们所调用的脚本。从脚本定义中,您可以看到这实际上用 node 启动了 JavaScript 文件 ./bin/www。它还定义了一个“devstart” 脚本,我们在调用 npm run devstart 时调用它。这将启动相同的 ./bin/www 文件,但使用 nodemon 调用而不是 node 。

  "scripts": {
    "start": "node ./bin/www",
    "devstart": "nodemon ./bin/www"
  },

www 文件

文件 /bin/www 是应用程序入口点!它做的第一件事是 require() “真实” 应用程序入口点(即项目根目录中的 app.js ),app.js 会设置并返回express() 应用程序的对象。

#!/usr/bin/env node

/**
 * Module dependencies.
 */

var app = require('../app');

注意: require() 是一个全局 node 函数,用于将模块导入当前文件。这里我们使用相对路径指定 app.js 模块,并省略可选的(.js)文件扩展名。

此文件中的其余代码,将设置一个 node 运行的 HTTP 服务器,并将应用 app 设置为特定的端口(在环境变量中定义,如果变量未定义,则定义为3000),并开始监听和报告服务器错误和连接。现在你并不需要知道代码的其他内容(这个文件中的所有内容都是 “样板文件” ),但如果你感兴趣,可以随时查看它。

app.js

此文件创建一个 express 应用程序对象(按传统命名为 app),使用各种设置和中间件,以设置应用程序,然后从模块导出应用程序。下面的代码只显示了文件的一部分,创建和导出应用程序对象的部分:

var express = require('express');
var app = express();
...
module.exports = app;

回到上面的 www 入口点文件,它是在导入该文件时,提供给调用者的这个 module.exports 对象。让我们详细了解 app.js 文件。首先,我们使用 require()将一些有用的node 库导入到文件中,其中包括我们先前使用 NPM 为应用程序下载的 express,serve-favicon,morgan,cookie-parser 和 body-parser;和path 库,它是解析文件和目录路径的核心 node 库。

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');

然后我们require()来自我们的路由目录的模块。这些模块/文件包含用于处理特定的相关“路由”集合(URL路径)的代码。当我们扩展骨架应用程序,例如列出图书馆中的所有书籍时,我们将添加一个新文件,来处理与书籍相关的路由。

var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');

注意: 此时我们刚刚导入了模块;我们还没有真正使用过它的路由(在文件的更下方一点将使用到路由)。

接下来,我们使用导入的 express 模块​​,创建应用程序 app 对象,然后使用它来设置视图(模板)引擎。引擎的设置有两个部分。首先我们设置 'views' 值,来指定模板将被存储的文件夹(在这种情况下是子文件夹 /views)。然后我们设置 'view engine' 的值,来指定模板库(在本例中为 “pug” )。

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');

下一组函数调用 app.use(),将中间件的库,添加到请求处理链中。除了我们之前导入的第三方库之外,我们还使用 express.static 中间件,来使 Express 提供在项目根目录下,/public 目录中的所有静态文件。

// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

现在所有其他中间件都已设置完毕,我们将(先前导入的)路由处理代码,添加到请求处理链中。导入的代码,将为网站的不同部分定义特定路由:

app.use('/', indexRouter);
app.use('/users', usersRouter);

注意: 上面指定的路径 ('/' and '/users'),被视为定义在导入文件中的路由前缀。因此,例如,如果导入的用户模块 users/profile定义了路由,则可以在 /users/profile中访问该路由。我们将在后面的文章中,详细讨论路由。

文件中的最后一个中间件,为错误和 HTTP 404 响应添加了处理程序方法。

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

Express 应用程序对象(app)现已完全完成配置。最后一步,是将其添加到模块导出(这允许它通过 /bin/www 导入)。

module.exports = app;

路由

路由文档/routes/users.js 如下所示(路由文件共享一个类似的结构,所以我们不需要也显示index.js)。首先加载 express 模块​​,并使用它获取 express.Router对象。然后它在该对象上指定一个路由,最后从模块中导出路由器(这就是允许将文件导入到 app.js 中的路由)。

var express = require('express');
var router = express.Router();

/* GET users listing. */
router.get('/', function(req, res, next) {
  res.send('respond with a resource');
});

module.exports = router;

该路由定义了一个回调,只要检测到具有正确模式的HTTP GET 请求,就会调用该回调。匹配模式是模块导入时指定的路由('/users'),加上('/')文件中定义的任何内容。换句话说,当收到/users/的 URL 时,将使用此路由。

提示: 尝试运行带有 node 的服务器,并在浏览器中访问以下 URL: http://localhost:3000/users/。您应该看到一条消息:'respond with a resource'。

上面有趣的事情是,回调函数有第三个参数 'next',因此是一个中间件函数,而不是简单的路由回调。虽然代码当前不使用 next 参数,但如果要在'/'根路由路径中,添加多个路由处理程序,将来可能会有用。

視圖(模板)

视图(模板)存储在 /views 目录中(如 app.js 中指定的)并且被赋予文件扩展名.pug。方法 Response.render()用于呈现指定的模板,以及在对象中传递的命名变量的值,然后将结果作为响应发送。在來自 /routes/index.js 的以下代码中,您可以看到,该路由如何使用模板 "index" 传递模板变量 "title" ,以呈现响应。

/* GET home page. */
router.get('/', function(req, res) {
  res.render('index', { title: 'Express' });
});

上面路由的相应模板在下面给出(index.pug)。我们稍后会详细讨论这个语法。您现在需要知道的是,标题变量title(值为 'Express')将插入模板中指定的位置。

extends layout

block content
  h1= title
  p Welcome to #{title}

挑战自己

/routes/users.js 中创建一个新路由,它将在 /users/cool/上显示文本 “You're so cool”。通过运行服务器,并在浏览器中访问 http://localhost:3000/users/cool/ 来测试它

总结

你现在为 本地图书馆 创建了一个骨架网站项目,并且用 node 验证了它能够运行。最重要的,你也理解了项目的结构,因此你也明白了我们需要为本地图书馆加上路由和视图。

接下来我们将开始修改骨架,让它能像一个图书馆网站一样运作。

 

另見

 

本系列教程

 

文档标签和贡献者

此页面的贡献者: edgar-chen
最后编辑者: edgar-chen,