Express/Node 简介

在第一篇 Express 文章中,我们回答了“什么是 Node?”和“什么是 Express?”的问题,并概述了 Express Web 框架的独特之处。我们将概述主要功能,并向您展示 Express 应用程序的主要构建块(尽管此时您还没有开发环境来对其进行测试)。

先决条件 服务器端网站编程 有基本的了解,尤其是对 网站中的客户端-服务器交互 的机制。
目标 熟悉 Express 是什么以及它如何在 Node 中发挥作用,它提供了哪些功能,以及 Express 应用程序的主要构建块。

介绍 Node

Node(或更正式地说,Node.js)是一个开源、跨平台的运行时环境,允许开发人员使用 JavaScript 创建各种服务器端工具和应用程序。运行时旨在在浏览器上下文之外使用(即直接在计算机或服务器操作系统上运行)。因此,该环境省略了浏览器特定的 JavaScript API,并添加了对更传统的操作系统 API 的支持,包括 HTTP 和文件系统库。

从 Web 服务器开发的角度来看,Node 具有许多优势

  • 性能出色!Node 被设计为优化 Web 应用程序的吞吐量和可扩展性,并且是许多常见 Web 开发问题的良好解决方案(例如实时 Web 应用程序)。
  • 代码是用“纯 JavaScript”编写的,这意味着在编写客户端和服务器端代码时,您将花费更少的时间处理语言之间的“上下文切换”。
  • JavaScript 是一种相对较新的编程语言,与其他传统 Web 服务器语言(如 Python、PHP 等)相比,它受益于语言设计方面的改进。许多其他新兴的流行语言会编译/转换为 JavaScript,因此您也可以使用 TypeScript、CoffeeScript、ClojureScript、Scala、LiveScript 等。
  • Node 包管理器 (npm) 提供对数十万个可重用包的访问。它还具有最佳的依赖关系解析功能,并且还可以用于自动化大多数构建工具链。
  • Node.js 是可移植的。它可在 Microsoft Windows、macOS、Linux、Solaris、FreeBSD、OpenBSD、WebOS 和 NonStop OS 上使用。此外,它得到了许多 Web 托管提供商的良好支持,这些提供商通常会提供用于托管 Node 网站的特定基础设施和文档。
  • 它拥有非常活跃的第三方生态系统和开发人员社区,并且有很多人愿意提供帮助。

您可以使用 Node.js 创建一个使用 Node HTTP 包的简单 Web 服务器。

Hello Node.js

以下示例创建了一个 Web 服务器,该服务器监听 URL http://127.0.0.1:8000/ 上的任何类型的 HTTP 请求 - 当收到请求时,脚本将响应字符串:“Hello World”。如果您已经安装了 node,您可以按照以下步骤尝试该示例

  1. 打开终端(在 Windows 上,打开命令行工具)
  2. 创建您要保存程序的文件夹,例如,test-node,然后通过在终端中输入以下命令来进入它
    bash
    cd test-node
    
  3. 使用您喜欢的文本编辑器,创建一个名为 hello.js 的文件,并将以下代码粘贴到其中
    js
    // Load HTTP module
    const http = require("http");
    
    const hostname = "127.0.0.1";
    const port = 8000;
    
    // Create HTTP server
    const server = http.createServer(function (req, res) {
      // Set the response HTTP header with HTTP status and Content type
      res.writeHead(200, { "Content-Type": "text/plain" });
    
      // Send the response body "Hello World"
      res.end("Hello World\n");
    });
    
    // Prints a log once the server starts listening
    server.listen(port, hostname, function () {
      console.log(`Server running at http://${hostname}:${port}/`);
    });
    
  4. 将文件保存在您上面创建的文件夹中。
  5. 返回终端并输入以下命令
    bash
    node hello.js
    

最后,在 Web 浏览器中导航到 https://127.0.0.1:8000;您应该在空白网页的左上角看到文本“Hello World”。

Web 框架

Node 本身并不直接支持其他常见的 Web 开发任务。如果您想为不同的 HTTP 动词(例如 GETPOSTDELETE 等)添加特定处理,分别处理不同 URL 路径(“路由”)上的请求,提供静态文件,或使用模板动态创建响应,那么 Node 本身将无济于事。您要么需要自己编写代码,要么可以避免重新发明轮子并使用 Web 框架!

介绍 Express

Express 是最流行的 Node.js Web 框架,是许多其他流行的 Node.js 框架的底层库。它提供机制来

  • 为不同 URL 路径(路由)上具有不同 HTTP 动词的请求编写处理程序。
  • 与“视图”渲染引擎集成,以便通过将数据插入模板来生成响应。
  • 设置常见的 Web 应用程序设置,例如用于连接的端口和用于渲染响应的模板位置。
  • 在请求处理管道中的任何点添加额外的请求处理“中间件”。

虽然Express 本身相当简约,但开发人员创建了兼容的中间件包来解决几乎所有 Web 开发问题。有用于处理 cookie、会话、用户登录、URL 参数、POST 数据、安全标头以及许多其他内容的库。您可以在 Express 中间件(以及一些流行的第三方包的列表)中找到由 Express 团队维护的中间件包列表。

注意:这种灵活性是一把双刃剑。有中间件包可以解决几乎所有问题或需求,但找出要使用的正确包有时可能是一个挑战。也没有“正确的方法”来构建应用程序,而且您在互联网上找到的许多示例都不是最佳的,或者只显示了您为了开发 Web 应用程序而需要做的事情的一小部分。

Node 和 Express 来自哪里?

Node 最初于 2009 年发布,仅适用于 Linux。npm 包管理器于 2010 年发布,并且在 2012 年添加了对 Windows 的原生支持。如果您想了解更多信息,可以深入研究 维基百科

Express 最初于 2010 年 11 月发布,目前处于 API 的主要版本 4。您可以查看 变更日志 以获取有关当前版本中更改的信息,以及 GitHub 以获取更详细的历史版本发布说明。

Web 框架的流行程度很重要,因为它表明它是否会继续得到维护,以及在文档、附加库和技术支持方面可能会提供哪些资源。

没有现成的、确定的服务器端框架流行程度衡量标准(尽管您可以使用一些机制来估计流行程度,例如计算每个平台的 GitHub 项目和 StackOverflow 问题的数量)。一个更好的问题是,Node 和 Express 是否“足够流行”以避免不受欢迎平台的问题。它们是否在不断发展?如果您需要帮助,您可以获得帮助吗?如果您学习 Express,您是否有机会获得报酬的工作?

基于使用 Express 的高知名度公司的数量、贡献代码库的人数以及提供免费和付费支持的人数,答案是肯定的,Express 是一个流行的框架!

Express 是有意见的框架吗?

Web 框架通常将自己称为“有主见”或“无主见”。

有主见框架是对“正确方法”处理任何特定任务有主见的框架。它们通常支持在特定领域(解决特定类型的问题)中快速开发,因为通常对做任何事情的正确方法都有很好的理解和记录。但是,它们在解决其主要领域之外的问题时可能不太灵活,并且往往在组件和方法的选择方面提供更少的选择。

相反,无主见框架对将组件粘合在一起以实现目标的最佳方式,甚至对应该使用哪些组件,都没有太多限制。它们使开发人员更容易使用最合适的工具来完成特定任务,尽管代价是您需要自己找到这些组件。

Express 是无主见的。您可以在请求处理链中插入几乎所有兼容的中间件,并且可以按您喜欢的任何顺序插入。您可以将应用程序构建在一个文件或多个文件中,并使用任何目录结构。您有时可能会觉得您有太多选择!

Express 代码长什么样?

在传统的基于数据的网站中,Web 应用程序会等待来自 Web 浏览器(或其他客户端)的 HTTP 请求。收到请求后,应用程序会根据 URL 模式以及可能包含在 POST 数据或 GET 数据中的相关信息来确定需要什么操作。根据需要,它可能会读取或写入数据库信息或执行满足请求所需的 other 任务。然后,应用程序将返回一个响应到 Web 浏览器,通常通过将检索到的数据插入 HTML 模板中的占位符来动态创建浏览器要显示的 HTML 页面。

Express 提供方法来指定特定 HTTP 动词(GETPOSTPUT 等)和 URL 模式(“路由”)调用哪个函数,以及方法来指定使用哪个模板(“视图”)引擎、模板文件的位置以及要使用哪个模板来渲染响应。您可以使用 Express 中间件来添加对 cookie、会话和用户、获取 POST/GET 参数等的支持。您可以使用 Node 支持的任何数据库机制(Express 不定义任何与数据库相关的行为)。

以下部分解释了使用ExpressNode 代码时会看到的一些常见内容。

Helloworld Express

首先,让我们考虑标准的 Express Hello World 示例(我们将在下面以及接下来的部分中讨论此示例的各个部分)。

注意:如果您已经安装了 Node 和 Express(或者按照 下一篇文章 中的说明安装它们),您可以在一个名为 app.js 的文本文件中保存此代码,然后通过调用以下命令在 bash 命令提示符中运行它

node ./app.js

js
const express = require("express");
const app = express();
const port = 3000;

app.get("/", function (req, res) {
  res.send("Hello World!");
});

app.listen(port, function () {
  console.log(`Example app listening on port ${port}!`);
});

前两行 require()(导入) express 模块并创建一个 Express 应用程序。这个对象通常命名为 app,它具有用于路由 HTTP 请求、配置中间件、渲染 HTML 视图、注册模板引擎以及修改 应用程序设置 的方法,这些设置控制应用程序的行为(例如环境模式、路由定义是否区分大小写等)。

代码的中间部分(从 app.get 开始的三行)显示了一个路由定义app.get() 方法指定了一个回调函数,每当有 HTTP GET 请求且路径('/')相对于网站根目录时,就会调用该函数。回调函数将请求和响应对象作为参数,并在响应上调用 send() 以返回字符串“Hello World!”。

最后一块代码在指定端口 ('3000') 上启动服务器,并在控制台中打印一条日志注释。在服务器运行后,您可以在浏览器中访问 localhost:3000 以查看返回的示例响应。

导入和创建模块

模块是一个 JavaScript 库/文件,您可以使用 Node 的 require() 函数将其导入到其他代码中。Express 本身就是一个模块,我们使用在 Express 应用程序中的中间件和数据库库也是模块。

以下代码展示了我们如何使用名称导入模块,以 Express 框架为例。首先,我们调用 require() 函数,指定模块名称为字符串 ('express'),并调用返回的对象来创建一个 Express 应用程序。然后,我们可以访问应用程序对象的属性和函数。

js
const express = require("express");
const app = express();

您也可以创建自己的模块,这些模块可以以相同的方式导入。

注意:想要创建自己的模块,因为这使您可以将代码组织成可管理的块——单一文件的整体应用程序很难理解和维护。使用模块也有助于管理命名空间,因为只有您显式导出的变量才会在使用模块时导入。

要使对象在模块外部可用,您只需要将它们作为 exports 对象上的附加属性公开。例如,下面的 square.js 模块是一个导出 area()perimeter() 方法的文件

js
exports.area = function (width) {
  return width * width;
};
exports.perimeter = function (width) {
  return 4 * width;
};

我们可以使用 require() 导入此模块,然后调用导出的方法,如下所示

js
const square = require("./square"); // Here we require() the name of the file without the (optional) .js file extension
console.log(`The area of a square with a width of 4 is ${square.area(4)}`);

注意: 您还可以指定模块的绝对路径(或名称,如我们最初所做的那样)。

如果您想在一个赋值中导出一个完整的对象,而不是逐个属性地构建它,请将其分配给 module.exports,如下所示(您也可以这样做,以使导出的对象的根成为构造函数或其他函数)

js
module.exports = {
  area(width) {
    return width * width;
  },

  perimeter(width) {
    return 4 * width;
  },
};

注意: 您可以将 exports 视为对 module.exports快捷方式,在给定的模块中。实际上,exports 只是一个变量,它在模块被评估之前被初始化为 module.exports 的值。该值是对一个对象的引用(在这种情况下为空对象)。这意味着 exports 持有对 module.exports 引用的相同对象的引用。这也意味着,通过将另一个值分配给 exports,它不再绑定到 module.exports

有关模块的更多信息,请参阅 Modules(Node API 文档)。

使用异步 API

JavaScript 代码经常使用异步 API 而不是同步 API 来执行可能需要一些时间才能完成的操作。同步 API 是指每个操作必须在下一个操作开始之前完成的 API。例如,以下日志函数是同步的,并且会按顺序(先,后)将文本打印到控制台。

js
console.log("First");
console.log("Second");

相比之下,异步 API 是指 API 将启动一个操作并立即返回(在操作完成之前)。操作完成后,API 将使用某种机制执行其他操作。例如,下面的代码将打印出“后,先”,因为即使 setTimeout() 方法首先调用并立即返回,操作也不会在几秒钟内完成。

js
setTimeout(function () {
  console.log("First");
}, 3000);
console.log("Second");

在 Node 中,使用非阻塞异步 API 比在浏览器中更重要,因为 Node 是一个单线程事件驱动的执行环境。“单线程”意味着对服务器的所有请求都在同一线程上运行(而不是被生成到单独的进程中)。这种模型在速度和服务器资源方面非常高效,但这确实意味着,如果您的任何函数调用需要很长时间才能完成的同步方法,它们将不仅阻塞当前请求,还会阻塞您的 Web 应用程序处理的每个其他请求。

异步 API 有多种方法可以通知您的应用程序它已完成。最常见的方法是在调用异步 API 时注册一个回调函数,该函数将在操作完成时回调。这是上面使用的做法。

注意: 如果您有一系列必须按顺序执行的依赖异步操作,使用回调可能会很“混乱”,因为这会导致多级嵌套回调。这个问题通常被称为“回调地狱”。可以通过良好的编码实践(参见 http://callbackhell.com/)、使用像 async 这样的模块,或者将代码重构为原生 JavaScript 功能(如 Promisesasync/await)来减少这个问题。Node 提供了 utils.promisify 函数以符合人体工程学地进行回调 → Promise 转换。

注意: Node 和 Express 的一个常见约定是使用错误优先回调。在此约定中,您的回调函数中的第一个值是一个错误值,而后续参数包含成功数据。这篇文章很好地解释了为什么这种方法很有用:The Node.js Way - Understanding Error-First Callbacks(fredkschott.com)。

创建路由处理程序

在我们的 Hello World Express 示例(见上文)中,我们为 HTTP GET 请求定义了一个(回调)路由处理程序函数,该函数指向站点根目录 ('/')。

js
app.get("/", function (req, res) {
  res.send("Hello World!");
});

回调函数以请求和响应对象作为参数。在本例中,该方法在响应上调用 send() 以返回字符串“Hello World!”。有很多 其他响应方法 用于结束请求/响应周期,例如,您可以调用 res.json() 发送 JSON 响应,或者调用 res.sendFile() 发送文件。

注意: 您可以在回调函数中使用任何您喜欢的参数名称;当回调被调用时,第一个参数始终是请求,第二个参数始终是响应。将它们命名为使您能够在回调的主体中识别您正在处理的对象是有意义的。

Express 应用程序对象还提供方法来为所有其他 HTTP 动词定义路由处理程序,这些方法主要以完全相同的方式使用

checkout(), copy(), delete(), get(), head(), lock(), merge(), mkactivity(), mkcol(), move(), m-search(), notify(), options(), patch(), post(), purge(), put(), report(), search(), subscribe(), trace(), unlock(), unsubscribe()

有一个特殊的路由方法 app.all(),它将在响应任何 HTTP 方法时被调用。这用于在特定路径上为所有请求方法加载中间件函数。以下示例(来自 Express 文档)展示了一个处理程序,它将针对对 /secret 的请求执行,无论使用的是哪种 HTTP 动词(只要它受 http 模块 支持)。

js
app.all("/secret", function (req, res, next) {
  console.log("Accessing the secret section…");
  next(); // pass control to the next handler
});

路由允许您匹配 URL 中的特定字符模式,并从 URL 中提取一些值,并将它们作为参数传递给路由处理程序(作为传递为参数的请求对象的属性)。

通常,将特定站点部分的路由处理程序分组在一起并使用公共路由前缀访问它们很有用(例如,具有 Wiki 的站点可能在一个文件中包含所有与 Wiki 相关的路由,并通过 /wiki/ 的路由前缀访问它们)。在 Express 中,这是通过使用 express.Router 对象实现的。例如,我们可以在名为 wiki.js 的模块中创建我们的 Wiki 路由,然后导出 Router 对象,如下所示

js
// wiki.js - Wiki route module

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

// Home page route
router.get("/", function (req, res) {
  res.send("Wiki home page");
});

// About page route
router.get("/about", function (req, res) {
  res.send("About this wiki");
});

module.exports = router;

注意:Router 对象添加路由就像向 app 对象添加路由一样(如前所述)。

要在我们的主应用程序文件中使用路由器,我们将 require() 路由模块 (wiki.js),然后在 Express 应用程序上调用 use() 以将路由器添加到中间件处理路径。然后,这两个路由将可以通过 /wiki//wiki/about/ 访问。

js
const wiki = require("./wiki.js");
// …
app.use("/wiki", wiki);

我们将在链接部分 Routes and controllers 中向您展示更多关于使用路由的信息,特别是关于使用 Router 的信息。

使用中间件

中间件在 Express 应用程序中被广泛使用,用于从提供静态文件到错误处理、压缩 HTTP 响应等各种任务。路由函数通过向 HTTP 客户端返回一些响应来结束 HTTP 请求-响应周期,而中间件函数通常对请求或响应执行一些操作,然后调用“堆栈”中的下一个函数,该函数可能是更多中间件或路由处理程序。中间件的调用顺序由应用程序开发人员决定。

注意: 中间件可以执行任何操作,执行任何代码,对请求和响应对象进行更改,它也可以结束请求-响应周期。如果它没有结束周期,那么它必须调用 next() 以将控制权传递给下一个中间件函数(否则请求将被挂起)。

大多数应用程序都将使用第三方中间件来简化常见的 Web 开发任务,例如处理 cookie、会话、用户身份验证、访问请求 POST 和 JSON 数据、日志记录等。您可以在 Express 团队维护的中间件包列表(其中还包括其他流行的第三方包)中找到这些包。其他 Express 包可在 npm 包管理器中获得。

要使用第三方中间件,您首先需要使用 npm 将其安装到您的应用程序中。例如,要安装 morgan HTTP 请求记录器中间件,您需要执行以下操作

bash
npm install morgan

然后,您可以在 Express 应用程序对象上调用 use() 将中间件添加到堆栈中

js
const express = require("express");
const logger = require("morgan");
const app = express();
app.use(logger("dev"));
// …

注意: 中间件和路由函数按声明顺序调用。对于某些中间件,顺序很重要(例如,如果会话中间件依赖于 cookie 中间件,那么 cookie 处理程序必须首先添加)。几乎总是将中间件放在设置路由之前调用,否则您的路由处理程序将无法访问您的中间件添加的功能。

您可以编写自己的中间件函数,并且您很可能需要这样做(即使只是为了创建错误处理代码)。中间件函数和路由处理程序回调之间的唯一区别是中间件函数具有第三个参数 next,中间件函数在没有完成请求周期时(当调用中间件函数时,它包含必须调用的下一个函数)预计会调用它。

您可以使用 app.use() 将中间件函数添加到所有响应的处理链中,或者使用关联的方法为特定 HTTP 动词添加中间件函数:app.get()app.post() 等。两种情况下,路由的指定方式相同,尽管在调用 app.use() 时路由是可选的。

以下示例展示了如何使用两种方法添加中间件函数,以及带/不带路由。

js
const express = require("express");
const app = express();

// An example middleware function
const a_middleware_function = function (req, res, next) {
  // Perform some operations
  next(); // Call next() so Express will call the next middleware function in the chain.
};

// Function added with use() for all routes and verbs
app.use(a_middleware_function);

// Function added with use() for a specific route
app.use("/someroute", a_middleware_function);

// A middleware function added for a specific HTTP verb and route
app.get("/", a_middleware_function);

app.listen(3000);

注意: 在上面,我们分别声明了中间件函数,然后将其设置为回调函数。在我们之前的路由处理函数中,我们在使用回调函数时声明了它。在 JavaScript 中,两种方法都是有效的。

Express 文档对 使用编写 Express 中间件有更多详细的说明。

提供静态文件

您可以使用 express.static 中间件来提供静态文件,包括您的图像、CSS 和 JavaScript(static() 是唯一真正属于Express 的中间件函数)。例如,您可以使用下面的代码行来提供图像、CSS 文件和 JavaScript 文件,这些文件来自名为“public”的目录,该目录与您调用 node 的位置处于同一级别。

js
app.use(express.static("public"));

public 目录中的任何文件都可以通过将文件名(相对于基本“public”目录)添加到基本 URL 来提供。例如

https://127.0.0.1:3000/images/dog.jpg
https://127.0.0.1:3000/css/style.css
https://127.0.0.1:3000/js/app.js
https://127.0.0.1:3000/about.html

您可以多次调用 static() 来提供多个目录。如果一个中间件函数找不到文件,它将被传递到下一个中间件(中间件调用的顺序基于您的声明顺序)。

js
app.use(express.static("public"));
app.use(express.static("media"));

您还可以为静态 URL 创建一个虚拟前缀,而不是将文件添加到基本 URL。例如,这里我们 指定了一个挂载路径,以便文件在带有前缀“/media”的情况下加载。

js
app.use("/media", express.static("public"));

现在,您可以从 /media 路径前缀加载 public 目录中的文件。

https://127.0.0.1:3000/media/images/dog.jpg
https://127.0.0.1:3000/media/video/cat.mp4
https://127.0.0.1:3000/media/cry.mp3

注意: 另请参阅 在 Express 中提供静态文件

处理错误

错误由一个或多个特殊的中间件函数处理,这些函数有四个参数,而不是通常的三个:(err, req, res, next)。例如

js
app.use(function (err, req, res, next) {
  console.error(err.stack);
  res.status(500).send("Something broke!");
});

这些函数可以返回任何所需的内容,但必须在所有其他 app.use() 和路由调用之后调用,以便它们是请求处理过程中的最后一个中间件!

Express 带有一个内置的错误处理程序,它处理应用程序中可能遇到的任何剩余错误。这个默认的错误处理中间件函数是在中间件函数栈的末尾添加的。如果您将错误传递给 next() 并且您没有在错误处理程序中处理它,它将由内置的错误处理程序处理;错误将与堆栈跟踪一起写入客户端。

注意: 堆栈跟踪不包含在生产环境中。要以生产模式运行它,您需要将环境变量 NODE_ENV 设置为“production”。

注意: HTTP404 和其他“错误”状态代码不会被视为错误。如果您想要处理这些代码,您可以添加一个中间件函数来执行此操作。有关更多信息,请参阅 常见问题解答

有关更多信息,请参阅 错误处理(Express 文档)。

使用数据库

Express 应用程序可以使用Node 支持的任何数据库机制(Express 本身没有定义任何关于数据库管理的特定额外行为/要求)。有很多选项,包括 PostgreSQL、MySQL、Redis、SQLite、MongoDB 等。

为了使用这些选项,您必须首先使用 npm 安装数据库驱动。例如,要安装流行的 NoSQL MongoDB 的驱动程序,您需要使用以下命令

bash
npm install mongodb

数据库本身可以安装在本地或云服务器上。在您的 Express 代码中,您需要驱动程序,连接到数据库,然后执行创建、读取、更新和删除 (CRUD) 操作。下面的示例(来自 Express 文档)展示了如何使用 MongoDB 查找“哺乳动物”记录。

这适用于 MongoDB 版本 ~ 2.2.33 的旧版本

js
const MongoClient = require("mongodb").MongoClient;

MongoClient.connect("mongodb://127.0.0.1:27017/animals", (err, db) => {
  if (err) throw err;

  db.collection("mammals")
    .find()
    .toArray((err, result) => {
      if (err) throw err;

      console.log(result);
    });
});

对于 MongoDB 版本 3.0 及更高版本

js
const MongoClient = require("mongodb").MongoClient;

MongoClient.connect("mongodb://127.0.0.1:27017/animals", (err, client) => {
  if (err) throw err;

  const db = client.db("animals");
  db.collection("mammals")
    .find()
    .toArray((err, result) => {
      if (err) throw err;
      console.log(result);
      client.close();
    });
});

另一种流行的方法是通过对象关系映射器 (“ORM”) 间接访问您的数据库。在这种方法中,您将数据定义为“对象”或“模型”,ORM 将这些数据映射到底层的数据库格式。这种方法的优势在于,作为开发人员,您可以继续以 JavaScript 对象的形式思考,而不是数据库语义,并且有一个明显的位置可以执行对传入数据的验证和检查。我们将在以后的文章中详细讨论数据库。

有关更多信息,请参阅 数据库集成(Express 文档)。

渲染数据(视图)

模板引擎(在Express 中也称为“视图引擎”)允许您在模板中指定输出文档的结构,使用占位符来表示生成页面时将填充的数据。模板通常用于创建 HTML,但也可用于创建其他类型的文档。

Express 支持多种模板引擎,最著名的是 Pug(以前称为“Jade”)、Mustache 和 EJS。每种引擎都有自己的优势,可以用来解决特定的用例(可以通过互联网搜索轻松找到相对比较)。Express 应用程序生成器使用 Jade 作为默认引擎,但也支持其他几种引擎。

在您的应用程序设置代码中,您设置要使用的模板引擎以及 Express 应该查找模板的位置,使用“views”和“view engine”设置,如下所示(您还需要安装包含您的模板库的包!)。

js
const express = require("express");
const path = require("path");
const app = express();

// Set directory to contain the templates ('views')
app.set("views", path.join(__dirname, "views"));

// Set view engine to use, in this case 'some_template_engine_name'
app.set("view engine", "some_template_engine_name");

模板的外观将取决于您使用的引擎。假设您有一个名为“index.<template_extension>”的模板文件,该文件包含名为“title”和“message”的数据变量的占位符,您将在路由处理函数中调用 Response.render() 来创建和发送 HTML 响应

js
app.get("/", function (req, res) {
  res.render("index", { title: "About dogs", message: "Dogs rock!" });
});

有关更多信息,请参阅 使用模板引擎与 Express(Express 文档)。

文件结构

Express 对结构或您使用的组件没有任何假设。路由、视图、静态文件和其他特定于应用程序的逻辑可以存在于任意数量的文件中,这些文件具有任意目录结构。虽然将整个Express 应用程序放在一个文件中是完全可能的,但通常根据功能(例如,帐户管理、博客、论坛)和架构问题域(例如,模型、视图或控制器,如果您恰好使用的是 MVC 架构)将应用程序拆分为文件更有意义。

在以后的主题中,我们将使用Express 应用程序生成器,它会创建一个模块化的应用程序骨架,我们可以轻松地扩展它来创建 Web 应用程序。

总结

恭喜您,您已经完成了 Express/Node 之旅的第一步!您现在应该了解 Express 和 Node 的主要优势,以及 Express 应用程序的主要部分大致是什么样子(路由、中间件、错误处理和模板代码)。您还应该了解,由于 Express 是一个无意见的框架,您将这些部分整合在一起的方式以及您使用的库在很大程度上取决于您!

当然,Express 故意是一个非常轻量级的 Web 应用程序框架,因此它的许多优势和潜力来自第三方库和功能。我们将在接下来的文章中更详细地讨论这些内容。在下一篇文章中,我们将研究如何设置 Node 开发环境,以便您可以开始看到一些实际的 Express 代码。

另请参阅