Express 教程第 2 部分:创建网站框架

我们《Express 教程》中的这篇第二篇文章展示了如何创建一个“骨架”网站项目,然后您可以继续用特定于站点的路由、模板/视图和数据库调用填充它。

先决条件 设置 Node 开发环境。回顾 Express 教程。
目标 能够使用Express 应用程序生成器启动您自己的新网站项目。

概述

本文介绍了如何使用Express 应用程序生成器工具创建“骨架”网站,然后您可以用特定于站点的路由、视图/模板和数据库调用填充它。在本例中,我们将使用该工具为我们的本地图书馆网站创建框架,稍后我们将向其中添加站点所需的所有其他代码。此过程非常简单,只需在命令行上使用新的项目名称调用生成器即可,还可以选择指定站点的模板引擎和 CSS 生成器。

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

注意

  • Express 应用程序生成器并不是 Express 应用程序的唯一生成器,生成的项目也不是构建文件和目录的唯一可行方法。但是,生成的站点确实具有易于扩展和理解的模块化结构。有关最小Express 应用程序的信息,请参阅Hello world 示例(Express 文档)。
  • Express 应用程序生成器使用var声明大多数变量。在本教程中,我们已将其中大多数更改为const(以及一些更改为let),因为我们希望演示现代 JavaScript 实践。
  • 本教程使用Express 应用程序生成器创建的package.json中定义的Express和其他依赖项的版本。这些不是(必然是)最新版本,您可能希望在将真实应用程序部署到生产环境时更新它们。

使用应用程序生成器

您应该已在设置 Node 开发环境的过程中安装了生成器。作为快速提醒,您可以使用 npm 包管理器全局安装生成器工具,如下所示

bash
npm install express-generator -g

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

bash
> express --help

    Usage: express [options] [dir]

  Options:

        --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 (dust|ejs|hbs|hjs|jade|pug|twig|vash) (defaults to jade)
        --no-view        use static html instead of view engine
    -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
    -h, --help           output usage information

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

bash
express

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

注意:选择模板引擎的其他选项(例如--hogan--ejs--hbs等)已弃用。使用--view(或-v)。

我应该使用哪个视图引擎?

Express 应用程序生成器允许您配置许多流行的视图/模板引擎,包括EJSHbsPug(Jade)、TwigVash,尽管如果您没有指定视图选项,它默认选择 Jade。Express 本身还可以支持大量其他模板语言开箱即用

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

一般来说,您应该选择一个能够提供您所需所有功能并让您更快地提高生产力的模板引擎——或者换句话说,就像您选择任何其他组件一样!比较模板引擎时需要考虑的一些事项

  • 生产力时间——如果您的团队已经具备某种模板语言的经验,那么他们很可能使用该语言更快地提高生产力。如果不是,那么您应该考虑候选模板引擎的相对学习曲线。
  • 流行度和活跃度——查看引擎的流行度以及它是否拥有活跃的社区。在网站的整个生命周期中,当出现问题时能够获得支持非常重要。
  • 样式——一些模板引擎使用特定的标记来指示“普通”HTML 中插入的内容,而其他模板引擎则使用不同的语法构建 HTML(例如,使用缩进和块名称)。
  • 性能/渲染时间。
  • 功能——您应该考虑您查看的引擎是否具有以下功能
    • 布局继承:允许您定义基本模板,然后仅“继承”您希望在特定页面中有所不同的部分。这通常比通过包含许多必需的组件或每次从头开始构建模板来构建模板更好的方法。
    • “包含”支持:允许您通过包含其他模板来构建模板。
    • 简洁的变量和循环控制语法。
    • 能够在模板级别过滤变量值,例如将变量大写或格式化日期值。
    • 能够生成除 HTML 之外的输出格式,例如 JSON 或 XML。
    • 支持异步操作和流。
    • 客户端功能。如果模板引擎可以在客户端上使用,则可以将所有或大部分渲染工作在客户端完成。

注意:互联网上有许多资源可以帮助您比较不同的选项!

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

我应该使用哪个 CSS 样式表引擎?

Express 应用程序生成器允许您创建一个配置为使用最常见的 CSS 样式表引擎的项目:LESSSASSStylus

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

与模板引擎一样,您应该使用能够让您的团队最有效率的样式表引擎。对于这个项目,我们将使用原生 CSS(默认值)作为我们的 CSS 需求,因为它们不够复杂,不值得使用其他任何东西。

我应该使用哪个数据库?

生成的代码不使用/包含任何数据库。Express 应用程序可以使用Node支持的任何数据库机制Express本身没有为数据库管理定义任何特定的额外行为/要求)。

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

创建项目

对于我们将要构建的示例本地图书馆应用程序,我们将使用Pug模板库和无 CSS 引擎创建一个名为express-locallibrary-tutorial的项目。

首先,导航到您想要创建项目的目录,然后在命令提示符中运行Express 应用程序生成器,如下所示

bash
express express-locallibrary-tutorial --view=pug

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

   create : express-locallibrary-tutorial\
   create : express-locallibrary-tutorial\public\
   create : express-locallibrary-tutorial\public\javascripts\
   create : express-locallibrary-tutorial\public\images\
   create : express-locallibrary-tutorial\public\stylesheets\
   create : express-locallibrary-tutorial\public\stylesheets\style.css
   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\error.pug
   create : express-locallibrary-tutorial\views\index.pug
   create : express-locallibrary-tutorial\views\layout.pug
   create : express-locallibrary-tutorial\app.js
   create : express-locallibrary-tutorial\package.json
   create : express-locallibrary-tutorial\bin\
   create : express-locallibrary-tutorial\bin\www

   change directory:
     > cd express-locallibrary-tutorial

   install dependencies:
     > npm install

   run the app (Bash (Linux or macOS))
     > DEBUG=express-locallibrary-tutorial:* npm start

   run the app (PowerShell (Windows))
     > $ENV:DEBUG = "express-locallibrary-tutorial:*"; npm start

   run the app (Command Prompt (Windows)):
     > SET DEBUG=express-locallibrary-tutorial:* & npm start

在输出的末尾,生成器提供了有关如何安装依赖项(如package.json文件中列出的)以及如何在不同操作系统上运行应用程序的说明。

注意:生成器创建的文件将所有变量定义为var。在继续之前,打开所有生成的文件并将var声明更改为const(本教程的其余部分假设您已这样做)。

运行网站框架

此时,我们有一个完整的骨架项目。该网站实际上还没有做太多事情,但值得运行它以表明它可以正常工作。

  1. 首先,安装依赖项(install命令将获取项目package.json文件中列出的所有依赖项包)。
    bash
    cd express-locallibrary-tutorial
    npm install
    
  2. 然后运行应用程序。
    • 在 Windows CMD 提示符下,使用以下命令
      batch
      SET DEBUG=express-locallibrary-tutorial:* & npm start
      
    • 在 Windows Powershell 中,使用以下命令
      powershell
      ENV:DEBUG = "express-locallibrary-tutorial:*"; npm start
      

      注意:本教程中未介绍 Powershell 命令(提供的“Windows”命令假设您正在使用 Windows CMD 提示符)。

    • 在 macOS 或 Linux 上,使用以下命令
      bash
      DEBUG=express-locallibrary-tutorial:* npm start
      
  3. 然后在浏览器中加载https://127.0.0.1:3000/以访问应用程序。

您应该会看到一个如下所示的浏览器页面

Browser for default Express app generator website

恭喜!您现在拥有一个可以通过端口 3000 访问的工作 Express 应用程序。

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

bash
SET DEBUG=express-locallibrary-tutorial:* & npm start
> [email protected] start D:\github\mdn\test\exprgen\express-locallibrary-tutorial
> node ./bin/www

  express-locallibrary-tutorial:server Listening on port 3000 +0ms
GET / 304 490.296 ms - -
GET /stylesheets/style.css 200 4.886 ms - 111

启用文件更改时服务器重启

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

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

bash
npm install --save-dev nodemon

如果您仍然选择将nodemon全局安装到您的机器上,而不仅仅是安装到项目的package.json文件中

bash
npm install -g nodemon

如果您打开项目的package.json文件,您现在将看到一个包含此依赖项的新部分

json
 "devDependencies": {
    "nodemon": "^3.1.3"
}

由于该工具不是全局安装的,因此我们无法从命令行启动它(除非我们将其添加到路径中),但我们可以从 npm 脚本中调用它,因为 npm 了解所有已安装的包。找到您的 package.json 的scripts部分。最初,它将包含一行,以"start"开头。通过在该行的末尾加上逗号并添加"devstart""serverstart"行来更新它

  • 在 Linux 和 macOS 上,scripts 部分将如下所示
    json
      "scripts": {
        "start": "node ./bin/www",
        "devstart": "nodemon ./bin/www",
        "serverstart": "DEBUG=express-locallibrary-tutorial:* npm run devstart"
      },
    
  • 在 Windows 上,“serverstart”值将如下所示(如果使用命令提示符)
    bash
    "serverstart": "SET DEBUG=express-locallibrary-tutorial:* & npm run devstart"
    

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

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

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

上面在package.json中的脚本中添加的serverstart命令就是一个很好的例子。使用这种方法意味着您不再需要键入很长的命令来启动服务器。请注意,添加到脚本中的特定命令仅适用于 macOS 或 Linux。

生成的项目

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

目录结构

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

express-locallibrary-tutorial
    app.js
    /bin
        www
    package.json
    package-lock.json
    /node_modules
        [about 6700 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文件定义了应用程序依赖项和其他信息

json
{
  "name": "express-locallibrary-tutorial",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "start": "node ./bin/www"
  },
  "dependencies": {
    "cookie-parser": "~1.4.4",
    "debug": "~2.6.9",
    "express": "~4.16.1",
    "http-errors": "~1.6.3",
    "morgan": "~1.9.1",
    "pug": "2.0.0-beta11"
  },
  "devDependencies": {
    "nodemon": "^3.1.3"
  }
}

scripts 部分首先定义了一个“start”脚本,当我们调用npm start启动服务器时,我们正在调用它(此脚本由Express Application Generator添加)。从脚本定义中,您可以看到这实际上是用node启动了JavaScript文件./bin/www

我们已经在启用文件更改时服务器重启中修改了此部分,添加了devstartserverstart脚本。这些可用于用nodemon而不是node启动相同的./bin/www文件(此版本的脚本适用于 Linux 和 macOS,如上所述)。

json
  "scripts": {
    "start": "node ./bin/www",
    "devstart": "nodemon ./bin/www",
    "serverstart": "DEBUG=express-locallibrary-tutorial:* npm run devstart"
  },

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

  • cookie-parser:用于解析 cookie 标头并填充req.cookies(基本上提供了一种访问 cookie 信息的便捷方法)。
  • debug:一个微型的 node 调试实用程序,其模型来自 node 核心调试技术。
  • morgan:一个用于 node 的 HTTP 请求记录器中间件。
  • http-errors:根据需要创建 HTTP 错误(用于 express 错误处理)。

生成项目中的默认版本有点过时了。将您的package.json文件的依赖项部分替换为以下文本,该文本指定了撰写本文时这些库的最新版本

json
  "dependencies": {
    "cookie-parser": "^1.4.6",
    "debug": "^4.3.5",
    "express": "^4.19.2",
    "http-errors": "~2.0.0",
    "morgan": "^1.10.0",
    "pug": "3.0.3"
  },

然后使用以下命令更新已安装的依赖项

bash
npm install

注意:定期更新到依赖库的最新兼容版本是一个好主意——这甚至可以作为持续集成设置的一部分自动或半自动地完成。

通常,库对次要版本和补丁版本的更新保持兼容。我们在上面每个版本前都添加了^,以便我们可以通过运行以下命令自动更新到最新的minor.patch版本

bash
npm update --save

主要版本会更改兼容性。对于这些更新,我们需要手动更新package.json和使用该库的代码,并对项目进行广泛的重新测试。

www 文件

文件/bin/www是应用程序的入口点!首先,它会require()“真实”的应用程序入口点(app.js,位于项目根目录),该入口点设置并返回express()应用程序对象。require()CommonJS 方法,用于将 JavaScript 代码、JSON 和其他文件导入到当前文件中。在这里,我们使用相对路径指定app.js模块,并省略可选的(.js)文件扩展名。

js
#!/usr/bin/env node

/**
 * Module dependencies.
 */

const app = require("../app");

注意:Node.js 14 及更高版本支持用于导入 JavaScript(ECMAScript)模块的 ES6 import 语句。要使用此功能,您必须将"type": "module",添加到您的 Express package.json文件中,应用程序中的所有模块都必须使用import而不是require(),并且对于相对导入,您必须包含文件扩展名(有关更多信息,请参阅Node 文档)。虽然使用import有很多好处,但本教程使用require()是为了与Express 文档相匹配。

此文件中的其余代码使用app设置为特定端口(在环境变量中定义,如果变量未定义则为 3000)设置节点 HTTP 服务器,并开始侦听和报告服务器错误和连接。目前,您实际上不需要了解有关代码的任何其他信息(此文件中的所有内容都是“样板代码”),但如果您有兴趣,可以随意查看它。

app.js

此文件创建一个express应用程序对象(根据惯例命名为app),使用各种设置和中间件设置应用程序,然后从模块导出应用程序。下面的代码仅显示创建和导出应用程序对象的文件部分

js
const express = require("express");
const app = express();
// …
module.exports = app;

回到上面的www入口点文件中,当导入此文件时,正是此module.exports对象被提供给调用方。

让我们详细介绍一下app.js文件。首先,我们使用require()将一些有用的 node 库导入到文件中,包括我们之前使用 npm 为应用程序下载的http-errorsexpressmorgancookie-parser;以及path,它是一个用于解析文件和目录路径的核心 Node 库。

js
const createError = require("http-errors");
const express = require("express");
const path = require("path");
const cookieParser = require("cookie-parser");
const logger = require("morgan");

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

js
const indexRouter = require("./routes/index");
const usersRouter = require("./routes/users");

注意:此时,我们只是导入了模块;我们还没有实际使用它的路由(这发生在稍后一点的文件中)。

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

js
const app = express();

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

下一组函数调用app.use()将我们上面导入的中间件库添加到请求处理链中。例如,express.json()express.urlencoded()需要填充req.body表单字段。在这些库之后,我们还使用express.static中间件,它使Express服务项目根目录中/public目录中的所有静态文件。

js
app.use(logger("dev"));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());

app.use(express.static(path.join(__dirname, "public")));

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

js
app.use("/", indexRouter);
app.use("/users", usersRouter);

注意:上面指定的路径('/'和'/users')被视为导入文件中定义的路由的前缀。例如,如果导入的users模块为/profile定义了一个路由,则您可以在/users/profile访问该路由。我们将在后面的文章中更多地讨论路由。

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

js
// catch 404 and forward to error handler
app.use((req, res, next) => {
  next(createError(404));
});

// error handler
app.use((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导入)。

js
module.exports = app;

路由

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

js
const express = require("express");
const router = express.Router();

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

module.exports = router;

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

注意:通过使用 node 运行服务器并在浏览器中访问 URL 来试用它:https://127.0.0.1:3000/users/。您应该看到一条消息:“respond with a resource”。

上面需要注意的一件事是回调函数具有第三个参数'next',因此它是一个中间件函数,而不是简单的路由回调。虽然代码目前没有使用next参数,但如果将来您想向'/'路由路径添加多个路由处理程序,它可能会很有用。

视图(模板)

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

js
/* GET home page. */
router.get("/", (req, res, next) => {
  res.render("index", { title: "Express" });
});

上面路由的对应模板如下所示(index.pug)。我们稍后会详细讨论语法。您现在只需要知道变量title(值为'Express')插入到模板中指定的位置。

pug
extends layout

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

挑战自己

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

总结

您现在已经为本地图书馆创建了一个骨架网站项目,并验证了它可以使用node运行。最重要的是,您也了解了项目的结构,因此您对需要在哪里进行更改以添加本地图书馆的路由和视图有一个很好的了解。

接下来,我们将开始修改骨架,使其作为图书馆网站工作。

另请参阅