← 返回蜂巢洞察

面向初学者的Node.js与Express.js使用指南——服务器、路由机制以及视图处理方法的详细讲解

Node.js是一种用于在浏览器之外执行JavaScript的运行时环境。无论是小型脚本还是大规模的后端应用程序,Node.js都提供了构建适用于各种环境的JavaScript应用所需的API和工具。 然而,学习Node.js可能会让人感到不知所措。由于有太多需要理解的API、包、工具和概念,人们很容易迷失方向。 正因为如此,这本书重点讲解了构建实际应用所需的基础Node.js知识,避免了不必要的干扰。你将了解到Node.js的工作原理,如何使用其内置的API,以及如何创建能够处理文件、URL、事件、HTTP请求等功能的应用程序。 你还会学习到Express.js是如何简化许多常见的服务器端任

Node.js是一种用于在浏览器之外执行JavaScript的运行时环境。无论是小型脚本还是大规模的后端应用程序,Node.js都提供了构建适用于各种环境的JavaScript应用所需的API和工具。

然而,学习Node.js可能会让人感到不知所措。由于有太多需要理解的API、包、工具和概念,人们很容易迷失方向。

正因为如此,这本书重点讲解了构建实际应用所需的基础Node.js知识,避免了不必要的干扰。你将了解到Node.js的工作原理,如何使用其内置的API,以及如何创建能够处理文件、URL、事件、HTTP请求等功能的应用程序。

你还会学习到Express.js是如何简化许多常见的服务器端任务的。我们会探讨路由、控制器、视图等其他概念,这些内容能帮助你更高效地组织和构建Web应用。

无论你是第一次接触后端开发,还是希望进一步提升自己的JavaScript技能,这本指南都能为你提供扎实且实用的基础知识,帮助你使用Node.js和Express.js进行开发。

目录

你应该已经了解的知识

本指南适用于那些已经掌握了基本JavaScript知识,并希望开始使用Node.js和Express.js构建Web应用程序的读者。如果你熟悉以下内容,那么阅读本指南会带来最大的帮助:

  • JavaScript的基础知识,包括变量、函数、数组和对象。

  • 基本的异步JavaScript编程,包括回调函数、Promise以及`async`和`await`关键字。

  • 基础的HTML知识,包括链接、表单和输入元素。

  • 如何使用终端来导航文件夹并执行命令。

你不需要具备使用Node.js或Express.js的先验经验。熟悉如何安装npm包会很有帮助,但示例中的步骤会指导你完成所需的操作。

你需要的工具

在开始之前,请确保你已经安装了以下软件:

  • Node.js 24.15.0或更高版本。

  • npm 11.14.0或更高版本。

你可以通过运行以下命令来检查自己安装的版本:

node --version & npm --version

Node.js本身包含了npm,但随节点版一起提供的npm版本可能比这里列出的版本要旧。请核对两个版本的编号,如有需要,请分别更新npm。CodeSweetly的包管理器使用指南会告诉你如何安装、更新和检查这些工具。

本指南中提到的文件创建命令假设你的环境中已经安装了`mkdir`和`touch`等命令行工具。在macOS或Linux系统中,这些工具通常存在于Bash或zsh中;在Windows系统中,则可以通过Git Bash使用;而在使用Windows Subsystem for Linux的情况下,也可以通过Linux shell来执行这些命令。当然,你也可以直接在代码编辑器中创建文件和文件夹。

让我们开始学习Node.js吧。

Node.js究竟是什么?

Node.js既不是一种框架,也不是一种编程语言。实际上,它是一个异步、事件驱动的开发环境,其中包含了以下组件:

  • JavaScript引擎(谷歌的V8引擎)

  • 模块系统(CommonJS和ES modules)

  • 操作系统APIs(如`os`模块)

  • 文件系统操作接口(`fs`模块)

  • 网络服务器功能(`http`、`net`、`https`模块)

  • 内存管理工具

  • 事件循环机制

这些组件共同构成了Node.js的核心架构,使得JavaScript能够在浏览器之外运行。

Node.js explained

上图展示了Node.js作为一个功能完备的运行时环境,它为在浏览器之外构建和运行各种规模的JavaScript应用程序提供了必要的基础设施——无论是服务器程序、命令行工具、脚本,还是全栈应用,都可以通过Node.js来实现。

让我们通过使用Node.js和Express.js来构建应用程序,从而探讨图片中强调的那些要点。首先,创建项目目录。

为你的项目创建一个新的目录

使用mkdir命令来创建新的项目目录,操作方法如下:

mkdir codesweetly-nodejs-app-001

注意:你可以使用任何你喜欢的名称。在本指南中,我们为了演示 purposes会使用codesweetly-nodejs-app-001这个名称。

之后,通过命令行进入你的项目目录。

cd codesweetly-nodejs-app-001

创建package.json文件

进入项目目录后,使用npm来初始化一个package.json文件。

npm init -y

接下来,使用以下命令删除package.json文件中可选的main字段:

npm pkg delete main

提示:package.json文件中的main字段用于指定当其他程序加载该包时应该执行的入口点。对于这个项目来说,我们并不需要这个字段,因为我们会直接运行我们的脚本;保留它也不会产生任何不良影响。

为什么选择Node.js?

JavaScript最初是为网页浏览器开发的,但其他环境也同样提供了执行它的基础设施。

Node.js在2009年发布时,为在网页浏览器之外运行JavaScript提供了可能。例如,我们可以创建一个可以在系统终端中运行的脚本。

1. 创建一个JavaScript文件

创建你希望让Node.js来执行的JavaScript文件。

在支持touch命令的shell环境中(如Bash或zsh),可以使用以下命令来创建文件;否则,也可以在代码编辑器中创建该文件:

touch console.js

2. 编写你的JavaScript程序

打开新创建的JavaScript文件,然后编写你的程序:

console.log("=== 来自CodeSweetly团队的问候! ===");
console.log("我们希望你在使用Node.js进行编程时能够享受到乐趣。");
console.log("感谢你成为CodeSweetly社区的一员。");
console.log("=== 继续编码,继续创造,继续发布你的作品吧。 ===");

3. 运行你的JavaScript程序

安装了Node.js之后,你就可以通过命令行使用node命令来运行Node.js应用程序了。

node console.js
  • node:用于运行Node.js脚本或REPL的命令。

  • console.js:你希望让Node.js执行的JavaScript文件。

你也可以将这个命令添加到项目《package.json》文件中的`"scripts"`字段中:

{
  "scripts": {
    "start": "node console.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  }
}

配置好这个脚本后,你就可以在终端中这样运行你的JavaScript程序:

npm run start

一旦你执行了这个脚本,Node就会将程序的输出显示在终端中。输出内容如下所示:

$ npm run start

> codesweetly-nodejs-app-001@1.0.0 start
> node console.js

=== 来自CodeSweetly团队的问候! ===
我们希望你能愉快地使用Node.js进行编程。
感谢你成为CodeSweetly社区的一员。
=== 继续编码,继续创造,继续发布你的作品吧。 ===

如你所见,我们成功地在浏览器之外运行了JavaScript程序。而这正是Node.js所能帮助我们做到的。Node.js是一个异步、事件驱动的运行环境,它允许我们在浏览器之外执行JavaScript代码。你还可以自动重新运行你的程序,接下来我们就来讨论如何实现这一点。

4. 自动重新运行你的JavaScript程序

默认情况下,每次你对JavaScript文件进行修改后,都需要手动重新运行它。

在不断修改代码的同时还要重复执行这些手动操作,确实会带来一些麻烦。幸运的是,Node提供了`--watch`选项来自动化这个过程:

node --watch filename.extension

上面的命令使用了`--watch`选项,使Node处于监控模式。这样一来,每当你在其依赖关系图中的任何文件上做出修改时,Node就会自动重新执行指定的脚本。

  • node:用于运行Node.js脚本或REPL的命令。

  • --watch:用于激活Node的监控模式的选项。

  • filename.extension:你需要让Node执行的脚本文件。

提示:在Windows、macOS或Linux系统中,可以使用`Ctrl + C`来停止正在运行的程序。

默认情况下,`--watch`选项会监控入口文件及其所依赖的所有模块。换句话说,这个命令会让Node监视以下内容:

  • 入口文件(`filename.extension`)

  • 入口文件导入的所有文件

  • 这些被导入文件又所依赖的所有文件

  • 以此类推,会监控整个依赖关系图中的所有文件

Node的默认监控模式不会监视与程序无关的文件。如果你想让Node监控其他文件,可以使用`--watch-path`选项:

node --watch-path=./src --watch-path=./tests filename.extension

在上面的命令中,`--watch-path`选项告诉Node要监控`src`和`tests`目录中的所有文件,即使这些文件并不在入口文件的依赖关系图中。

  • node:用于运行 Node.js 脚本或 REPL 的命令。

  • --watch-path:此参数用于指定 Node 应该监控哪些路径以检测文件变化。

  • filename.extension:你需要让 Node 执行的脚本文件。

提示:

  • 使用 --watch-path 时,Node 会忽略依赖关系图中的其他模块,除非这些模块位于你指定的路径范围内。因此,Node 只会监控你明确指出的路径,而不会检查其他地方。

  • 单独使用 也会使 Node 进入监控模式,但仍然只会监控你指定的路径。

  • --watch-path 在 macOS 和 Windows 上是可用的语法,但在 Linux 上不可用。在 Linux 上,应使用 --watch 来监控主入口文件及其依赖项。

  • 监控命令并不会自动刷新浏览器。它们只会检测文件是否发生变化,然后让 Node 重新运行你指定的程序。因此,如果你的程序需要向浏览器发送数据,你需要手动刷新浏览器或使用第三方工具来实现自动刷新。

Node.js 支持两种模块系统,让我们来了解一下它们。

Node 的模块支持

Node.js 同时支持 CommonJS 模块(扩展名为 .cjs)和 ECMAScript 模块(扩展名为 .mjs)。这意味着你可以根据自己的喜好选择使用哪种模块类型来构建 Node.js 应用程序。

例如,下面的文件是一个 script.mjs JavaScript 文件。由于它的扩展名是 .mjs,Node 会将其视为 ECMAScript 模块。

script.mjs

import http from "node:http";

而另一个文件 script.cjs,由于扩展名是 .cjs,Node 会将其视为 CommonJS 模块。

script.cjs

const http = require("node:http");

如果你想为项目中的所有 .js 文件指定具体的模块类型,可以在 package.json 文件中设置 type 字段,如下所示:

package.json

{
  "scripts": {
    "start": "node console.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "type": "module",
  "license": "ISC"
}

在上述代码中,"type": "module" 这一设置会使 Node 将受此 package.json 文件控制的 .js 文件视为 ES 模块。而嵌套的 package.json 文件则会单独定义其模块类型。.mjs 和 .cjs 文件仍然保持它们原有的模块类型。

如果想将某个范围内的 .js 文件视为 CommonJS 模块,只需将 "type" 字段设置为 "commonjs" 即可。

一些注意事项:

  • ES模块系统是JavaScript的官方标准模块系统。

  • 在JavaScript尚未拥有官方标准的模块系统时,Kevin Dangoor于2009年1月启动了这个项目,后来这个项目发展成了CommonJS。

  • ES模块在2015年被纳入ECMAScript标准中。而CommonJS仍然在Node.js中得到支持。

在本指南中,我们主要会使用带有ES模块的Node.js,因为ES模块确实是JavaScript的标准模块系统。为了能够按照示例进行操作,请确保你的`package.json`文件中的`"type"`字段被设置为`"module"`。你可以通过在项目目录中运行以下命令来设置这个值:

npm pkg set type=module

在适当的情况下,我们也会对比CommonJS的语法。

现在,让我们来讨论如何使用Node.js创建能够接收并响应浏览器请求的Web服务器吧。

使用Node.js创建HTTP服务器

HTTP服务器允许你的JavaScript应用程序处理来自客户端(如浏览器)的请求,并向它们发送响应。

配置应用程序的HTTP服务器主要有三个步骤:

  1. 创建一个新的HTTP `Server` 对象实例。

  2. 指定服务器应运行的系统端口。

  3. 定义服务器应该如何响应客户端的请求。

让我们详细了解一下这三个步骤。首先,为你的项目创建一个ES模块。

touch server.js

接下来,打开新创建的模块文件,并初始化一个新的HTTP `Server` 对象实例。

初始化新的HTTP `Server` 对象实例

Node.js提供了`createServer`方法来创建HTTP `Server` 对象的实例。

你可以通过从Node.js的`http`模块中导入这个方法来在项目中使用它,具体操作如下:

server.js

import { createServer } from "node:http";

const server = createServer();

这里的具体原理是:

  • `import`语句:将Node.js的`http`库中的`createServer`方法导入到`server.js`这个ES模块中。

  • `server`:这是一个变量,用于存储`createServer()函数返回的HTTP `Server`对象。

以下是使用CommonJS的方式:

server.cjs

const http = require("node:http");

const server = http.createServer();

一旦创建了本地HTTP `Server`对象,接下来就需要指定服务器应运行的端口。

配置服务器应运行的系统端口

HTTP `服务器` 对象提供了一个 `listen()` 方法,用于配置服务器应运行的端口以及接收客户端请求的端口。

语法

import { createServer } from "node:http";

const server = createServer();

server.listen(port, hostname, backlog, callback);

`listen()` 方法接受以下参数:

  • port:(数字)服务器应运行的端口。如果省略,操作系统会分配一个未使用的端口。你可以通过 `server.address().port` 来获取服务器在开始监听客户端连接后所使用的端口。

  • hostname:(字符串)服务器应接收客户端连接的主机名或 IP 地址。如果省略,服务器会默认使用未指定的 IPv6 地址 或 IPv4 地址。这些未指定的地址会绑定到适用于该地址族的所有网络接口上。你可以通过 `server.address().address` 来获取服务器在开始监听客户端连接后所使用的 IP 地址。

  • backlog:(数字)待处理连接队列的最大长度。默认值为 511。

  • callback:(函数)当服务器开始监听客户端请求时,会执行的函数。

示例

server.js

import { createServer } from "node:http";

const server = createServer();

server.listen(3000, "127.0.0.1", 511, () => {
  const info = server.address();
  console.log(`服务器运行在 http://${info.address}:
${info.port}/`);
});

具体操作过程如下:

  • import 语句:将 Node.js 的 `http` 库中的 `createServer` API 导入到 `server.js` 文件中。

  • server:用于存储由 `createServer()` 函数返回的 HTTP `服务器` 对象的变量。

  • server.listen():启动服务器以开始监听客户端连接。提示:当服务器成功开始运行时,该方法会触发一个 listening 事件。

以下是使用 CommonJS 的写法:

server.cjs

const http = require("node:http");

const server = http.createServer();

server.listen(3000, "127.0.0.1", 511, () => {
  const info = server.address();
  console.log(`服务器运行在 http://${info.address}:
${info.port}/`);
});

什么是127.0.0.1这个地址?

127.0.0.1这个地址是一个IPv4回环地址,它指的是你的本地计算机。主机名localhost也指的是你的本地计算机,但它可能会解析为127.0.0.1,或者IPv6回环地址::1。你可以这样使用它:

server.js

import { createServer } from "node:http";

const server = createServer();

server.listen(3000, "localhost", 511, () => {
  console.log("服务器正在运行,地址为http://localhost:3000/");
});

如果你运行这个服务器并访问它的网址,浏览器会等待响应,因为我们还没有设置请求处理程序。现在我们就来配置它吧。

配置服务器以响应客户端的请求

createServer()方法接受一个requestListener回调函数,每当服务器收到HTTP请求时,这个回调函数会自动被调用。Node.js允许你使用这个回调函数来响应请求。

语法

createServer()方法接受两个可选参数。其语法如下:

import { createServer } from "node:http";

const server = createServer(options, callback);
  • options:一个用于自定义服务器行为的对象。

  • callback:用于处理和响应客户端请求的requestListener函数。它接受两个参数:

import { createServer } from "node:http";

const server = createServer(options, function (request, response) {
  // requestListener函数的主体代码
});
  • request参数:一个http.IncomingMessage对象,其中包含了关于客户端请求的详细信息。

  • response参数:一个http.ServerResponse对象,用于响应客户端的请求。

将requestListener回调函数作为createServer的第二个参数传递给它,会使Node.js自动将其注册为对"request"事件的监听器。因此,上面的语法等价于:

import { createServer } from "node:http";

const server = createServer(options);

server.on("request", function (request, response) {
  // requestListener函数的主体代码
});

server.on("request", callback)这条命令告诉服务器要监听请求事件,并在发生此类事件时执行指定的回调函数。

示例

server.js

// 添加 Node.js HTTP 模块
import { createServer } from "node:http";

// 指定服务器运行的主机名和端口号
const hostname = "localhost";
const port = 3000;

// 使用请求监听回调函数创建一个新的 Server 实例
const server = createServer((req, res) => {
  res.statusCode = 200; // 设置成功的响应状态码为 200
  res.setHeader("Content-Type", "text/plain"); // 定义响应数据的媒体类型
  res.end("Hello World!"); // 指定响应数据并关闭响应流
});

// 在指定的端口和主机名上运行服务器
server.listen(port, hostname, () => {
  console.log(`服务器正在以下地址运行:http://${hostname}:${port}/`);
});

上述代码片段使用了requestListener回调函数中的响应参数来配置服务器,使其能够响应客户端的请求。以下是另一种使用server.on("request", callback)的方法:

server.js

// 添加Node.js HTTP模块
import { createServer } from "node:http";

// 指定服务器运行的主机名和端口
const hostname = "localhost";
const port = 3000;

// 创建一个新的Server实例
const server = createServer();

// 监听请求事件并执行requestListener回调函数
server.on("request", (req, res) => {
  res.statusCode = 200; // 设置成功的响应状态码为200
  res.setHeader("Content-Type", "text/plain"); // 定义响应数据的媒体类型
  res.end("Hello World!"); // 指定响应数据并关闭响应流
});

// 在指定的端口和主机名上运行服务器
server.listen(port, hostname, () => {
  console.log(`服务器正在http://${hostname}:
${port}/运行`);
});

现在你的服务器已经设置完成,端口也配置好了,你可以运行这个应用程序了。

node server.js
  • node:用于运行Node.js脚本或REPL的命令。

  • server.js:你需要让Node运行的JavaScript文件。

当服务器正在运行时,如果用户通过服务器的网址请求应用程序的资源,他们将会看到“Hello World!”这样的响应结果,如下图所示。

浏览器在http://localhost:3000/显示"Hello World!"文本

提示:在Windows、macOS或Linux系统中,可以使用Ctrl + C来停止正在运行的进程。

虽然node:http模块是Node.js用于处理HTTP请求的原生API,但开发人员通常会使用框架来简化这一过程。一些流行的Node.js Web框架包括Express.js、Fastify和Koa.js。我们就以Express.js为例,来看看框架是如何简化Node中HTTP请求处理的。

Express.js究竟是什么?

Express.js是一个Web框架,它能够简化Node.js中的服务器端HTTP请求处理流程。该框架扩展了Node的HTTP模块,提供了路由和中间件等内置功能,帮助开发人员高效地构建Web应用程序和API,而无需编写重复的代码。下面将介绍如何在你的Node.js项目中使用它。

1. 安装Express

npm install express@5.2.1

2. 使用Express.js配置项目的Node.js Web服务器

<打开JavaScript服务器文件,使用Express.js来搭建一个Web服务器:

server.js
// 添加 Express 模块
import express from "express";

// 创建一个新的 Express 应用程序实例
const app = express();

// 设置服务器运行的端口号
const port = 3000;

// 为 “/” 路由创建一个处理 GET 请求的函数
app.get("/", (req, res) => {
  res.send("Hello, world!");
});

// 在指定的端口上运行服务器
app.listen(port, () => {
  console.log(`服务器正在 http://localhost:${port} 上运行`);
});

上述代码的作用如下:

  • 创建一个 Express 应用程序实例,以便使用 Express 提供的各种 API(如路由处理器和中间件),从而简化 Node.js 中的 HTTP 请求处理流程。

  • 使用 Express 的 `app.get()` 方法来定义服务器如何处理发往 “/” 路由的 HTTP GET 请求。

  • 使用 Express 的 `res.send()` 方法发送 HTTP 响应,并根据数据类型自动设置相应的响应头信息。

  • 使用 Express 的 `app.listen()` 方法在指定的端口上启动 Node.js HTTP 服务器;当服务器开始接收客户端请求时,会执行相应的回调函数。

提示:

  • 在语句 `app.get("/", (req, res) => res.send("Hello, world!"))` 中:

  • `app.get()` 是 Express 的路由处理方法。

  • `"/"` 表示要处理的路由路径。

  • ...` 是用于处理请求的函数。

  • 整个语句就是一条路由定义。

  • 传递给 Express 路由处理函数的参数 `req` 和 `res` 实际上是 Node.js 中 `http.IncomingMessage` 和 `http.ServerResponse` 对象的增强版本。Express 为这些对象添加了一些属性和辅助方法,从而简化了 HTTP 请求与响应的处理过程。

  • 路由定义的顺序很重要。Express 会按照定义顺序依次处理匹配的请求。某个处理函数可以通过调用 `next()` 将控制权传递给下一个处理函数。

  • 3. 运行 Express.js Web 服务器

    要运行一个 Node.js 脚本(包括使用 Express.js 处理 HTTP 请求的脚本),只需在命令行中输入 `node` 后跟脚本文件名即可。

    node server.js

    在浏览器中访问 `http://localhost:3000`,就可以看到 “Hello, world!” 这一输出结果。

    提示:在 Windows 上使用 `Ctrl + C`,在 macOS 上使用 `Control + C` 可以停止服务器运行。

    原始的 Node.js 代码与 Express.js 代码

    虽然可以使用 Node.js 原生的 HTTP API 来处理 Web 请求,但这样做需要手动进行很多配置工作,比如检查每个请求的方法和 URL、设置响应头信息,以及将数据对象转换为 JSON 格式等。

    Express将重复出现的请求处理逻辑抽象为声明式方法,从而简化了Node.js Web服务器的开发过程。

    下面是两个示例,分别展示了使用原生HTTP模块和Express方法编写Node.js Web服务器代码的差异。

    示例1:使用原始Node.js处理Web请求

    下面的Web服务器使用了Node内置的HTTP模块来处理对“首页”(/)和“书籍”(/books)这两个端点的GET请求;对于不匹配的请求,它会返回404错误响应。

    server.js

    import { createServer } from "node:http";
    const port = 3000;
    
    const server = createServer((req, res) => {
      if (req.method === "GET" && req.url === "/") {
        res.writeHead(200, { "Content-Type": "text/plain" });
        res.end("欢迎来到首页!");
      } else if (reqmethod === "GET" && req.url === "/books") {
        const books = [
          { id: 1, name: "Code React Sweetly" },
          { id: 2, name: "Creating NPM Package" },
        ];
        res.writeHead(200, { "Content-Type": "application/json" });
        res.end(JSON.stringify(books));
      } else {
        res.writeHead(404, { "Content-Type": "text/plain" });
        res.end("页面未找到");
      }
    });
    
    server.listen(port, () => {
      console.log(`原始Node服务器运行在http://localhost:${port}`);
    });
    

    上述代码示例使用了Node的原生http模块来构建一个Web服务器,该服务器能够接收并响应浏览器的连接请求。在这个例子中,你需要手动完成以下操作:

    • 路由匹配: if (req.method === "..." && req.url === "...")

    • 响应头设置: res.writeHead(...)

    • JSON数据格式化: JSON.stringify(...)

    现在,让我们看看如何使用Express来构建类似的Web服务器。请逐一运行这些示例,替换server.js中的代码内容,然后重新启动服务器。

    示例2:使用Express.js处理Web请求

    下面的Web服务器使用了Express的路由功能来处理对“首页”(/)和“书籍”(/books)这两个端点的GET请求;对于不匹配的请求,它会返回404错误响应。

    server.js

    import express from "express";
    const app = express();
    const port = 3000;
    
    app.get("/", (req, res) => {
      res.send("欢迎来到首页!");
    });
    
    app.get("/books", (req, res) => {
      const books = [
        { id: 1, name: "Code React Sweetly" },
        { id: 2, name: "Creating NPM Package" },
      ];
      res.json(books);
    });
    
    app.use((req, res) => {
      res.status(404).send("页面未找到");
    });
    
    app.listen(port, () => {
      console.log(`Express服务器运行在http://localhost:${port}`);
    });
    

    上述代码片段使用了Express.js来构建一个Web服务器,该服务器能够接收并响应浏览器的连接请求。Express简化了以下各项任务:

    • 路由逻辑: Express.js提供了诸如app.get()、app.post()和app.delete()这样的简单路由声明方式,这样就无需为每条路由都编写复杂的条件逻辑来检查req.url和req.method了。

    • 响应头配置: Express的res.send()方法会根据响应内容自动设置相应的响应头信息。当需要设置状态码时,可以使用res.status()方法,就像上面的404错误处理示例那样。

    • JSON格式化: Express的res.json()方法会自动将JavaScript对象转换为有效的JSON格式响应数据,同时负责序列化和响应头的设置工作。

    在Node.js中创建Express.js Web服务器时需要了解的重要事项

    在您的Node.js项目中创建Express.js服务器时,请记住以下关键点。

    通常会最后调用app.listen()

    在Express.js应用中,人们通常会将app.listen()方法放在最后调用。这样,在服务器开始监听请求之前,所有的路由、中间件以及错误处理机制都可以被正确配置完毕。

    您可以使用环境变量来设置端口号

    如果想要从环境变量中获取端口号,可以将现有的port声明替换为以下代码:

    const port = process.env.PORT || 3000;
    

    上述代码会让Node使用PORT环境变量的值作为端口号;如果该变量未设置或为空,系统会自动使用默认值3000。在Bash这样的shell环境中,可以使用以下命令为当前服务器运行设置端口号为8000:

    PORT=8000 node server.js
    

    常见的Express.js响应方法

    以下是一些在Express.js中常用的响应方法:

    • res.send():向客户端发送HTTP响应,并根据该方法传入的数据类型自动设置相应的响应头信息。

    • res.json():向客户端发送JSON格式的响应数据,同时会将响应头的Content-Type设置为application/json。

    • res.redirect():将客户端的请求重定向到另一个URL地址。

    • res.render():使用模板引擎生成视图内容,然后将生成的HTML字符串发送给客户端。

    • res.status():设置响应的HTTP状态码,但不会结束请求-响应流程。您可以在其后链式调用其他响应方法,例如res.status(404).send("404错误:页面未找到")。如果想要使用默认的状态码200,则无需调用此方法。

    • res.end():结束响应流程。如果没有传入数据参数,该方法不会发送任何额外的响应内容;但如果需要,在结束响应之前也可以传递数据信息。

    Express的响应方法并不会终止HTTP请求处理函数的执行

    虽然像res.send()这样的响应方法会关闭HTTP请求-响应循环,但它们并不会结束路由处理函数的执行。

    下面来看一个例子:

    app.get("/", (req, res) => {
      // 这行代码会向客户端发送文本响应,并关闭请求-响应循环,但不会终止路由处理函数的执行
      res.send("Hello, client!”
    
      // 这行代码会将字符串输出到控制台,因为路由处理函数仍在运行中
      console.log("Hello, devs!)
    
      // 这行代码会导致错误,因为请求-响应循环已经关闭,在回调函数执行期间无法再向客户端发送响应
      res.send("Hello, client again!");
    });
    

    在上面的例子中,console.log()语句能够正常执行,是因为路由处理函数仍在运行。而res.send("Hello, client!")这一行代码只是结束了对该请求的响应处理,并没有停止回调函数的执行。

    需要注意的是,上面示例中的app.get(...)代码实际上就是Express中的一个路由。那么,在Express.js中,路由到底是什么呢?让我们来了解一下。

    Express.js中的路由是什么?

    在Express.js中,一个路由由一个HTTP方法、一个URL路径,以及一组用于处理客户端请求的中间件函数组成。

    提示:

    • 一个端点由一个HTTP方法和一个URL路径构成。(端点 = HTTP方法 + URL路径)

    • 一个路由包括一个HTTP方法、一个URL路径,以及用于处理客户端请求的服务器端逻辑。(路由 = 服务器端的HTTP方法 + 服务器端的URL路径 + 处理函数)

    Express中路由的语法结构

    app.METHODPATH, HANDLER);
    
    • app:表示一个Express应用程序实例,它提供了对Express相关API的访问权限,包括各种方法和中间件,从而简化了在Node.js环境中处理HTTP请求的过程。

    • METHOD:表示一个 lowercase形式的HTTP请求方法,例如get、post或delete。

    • PATH:表示该路由将要处理的URL路径。它可以是一个字符串,也可以是一个正则表达式。Express 4还支持使用字符串模式进行匹配,不过在Express 5中这种语法有所不同。

    • HANDLER:当请求的HTTP方法与路由定义中的方法相匹配时,Express会执行这个回调函数或中间件函数。处理函数可以是一个单独的函数,也可以是多个函数的组合,甚至是可以是一个函数数组与一些参数的组合。

    提示:

    • 你可以使用app.all()方法来为特定路径上所有支持的HTTP请求方法设置处理函数。

    • app.use()方法允许你为所有HTTP请求方法和路径设置中间件函数。如果你指定了路径参数,app.use()会匹配该路径及其子路径。例如,/book会匹配/book、/book/dashboard以及/book/dashboard/author/202605。

    中间件与路由处理函数

    开发者通常用“路由处理函数”来指代负责解析请求并发送响应的最终函数,而那些可重用的前置函数则被称为“中间件”。

    app.METHOD PATH, MIDDLEWARE1, MIDDLEWARE2, HANDLER);

    注意:Express并不要求用于路由的函数必须具有不同的名称或分类。在Express看来,这些函数都属于处理函数,而next()则用于将控制权传递给下一个函数。不过,开发者通常会区分中间件和路由处理函数,以便更好地组织代码、提高代码的可读性并便于沟通。

    • 中间件函数通常存储在/middlewares目录中,它们充当执行跨领域任务或准备工作的实用组件,例如身份验证、数据验证、日志记录以及数据加载等。这些函数通常是可重用的,并且会通过调用next()将控制权传递给下一个函数。

    • 路由处理函数有时会被组织在/controllers目录中的控制器中,它们包含生成端点响应的业务逻辑,例如getUserProfile、createBlogPost或deleteAccount等。这些函数通常是针对特定路径设计的,并且会通过调用res.send()或res.json()等方法来完成请求-响应周期。

    Express.js中的路由示例

    以下是一些Express.js中的路由示例。

    使用单个处理函数的Express路由

    app.get("/single/handler", (req, res) => {
      res.send("请求由单个处理函数进行处理!");
    });
    

    上面的示例使用了一个单一的回调函数来处理对/single/handler端点的GET请求。在这种情况下,这个处理函数就是一个内嵌的控制器。一种常见的做法是将控制器放在controllers目录中,这样既可以方便代码的测试和维护,又能保持路由定义的清晰性。

    下面是一个具体的例子:

    以下示例展示了一个使用模块化控制器处理的GET /single/handler路由。该控制器被单独放在一个模块中(controllers/single-handler.js)。这种设计将路由定义与生成响应的处理逻辑分离开来。

    import express from "express";
    import * as controller from "./controllers/single-handler.js";
    
    const app = express();
    const port = 3000;
    
    app.get("/single/handler", controller.singleHandler);
    
    app.listen(port, () => {
      console.log(`Express服务器运行在http://localhost:${port}`);
    });
    

    下面是一个仅包含singleHandler处理函数的控制器文件示例。

    controllers/single-handler.js

    function singleHandler(req, res) {
      res.send("请求通过单个处理函数得到了处理!");
    }
    
    export { singleHandler };
    

    提示:控制器是一组用于处理特定端点请求的处理函数。为了提高代码的组织性并明确划分职责,控制器通常会被放在单独的模块中。

    使用多个中间件函数的Express路由

    app.get(
      "/multiple/middleware",
      (req, res, next) => {
        console.log("第一个中间件:将控制权传递给下一个中间件>");
        next();
      },
      (req, res, next) => {
        console.log("第二个中间件:将控制权传递给下一个中间件>"
        next();
      },
      (req, res) => {
        res.send("请求通过多个中间件函数得到了处理!");
      },
    );
    

    上述示例使用了多个中间件函数来处理对/multiple/middleware路径的GET请求。

    使用中间件函数数组的Express路由

    const arrayOfCallbacks = [
      function callback1(req, res, next) {
        console.log("第一个中间件:将控制权传递给下一个中间件>"
        next();
      },
      function callback2(req, res, next) {
        console.log("第二个中间件:将控制权传递给下一个中间件>"
        next();
      },
      function callback3(req, res) {
        res.send("请求通过中间件函数数组得到了处理!");
      },
    ];
    
    app.get("/array/middleware", arrayOfCallbacks);
    

    上述示例使用了一个中间件函数数组来处理对/array/middleware端点的GET请求。

    结合中间件函数数组与单独回调函数的Express路由

    const arrayOfCallbacks = [
      function callback1(req, res, next) {
        console.log("第一个回调函数:将控制权传递给下一个中间件>");
        next();
      },
      function callback2(req, res, next) {
        console.log("第二个回调函数:将控制权传递给下一个中间件>"
        next();
      },
    ];
    
    app.get(
      "/mix/middleware",
      arrayOfCallbacks,
      (req, res, next) => {
        console.log("第三个回调函数:将控制权传递给下一个中间件>"
        next();
      },
      (req, res, next) => {
        console.log("第四个回调函数:将控制权传递给下一个中间件>"
        next();
      },
      (req, res) => {
        res.send(
          "请求是通过一组中间件函数以及独立的回调函数来处理的!"
        );
      },
    );
    

    上述示例同时使用了回调函数数组和单独的函数参数来处理对/mix/middleware路径的GET请求。

    关于next参数需要了解的重要事项

    • next()会将控制权传递给栈中下一个中间件函数。

    • next("route")会跳过当前路由的剩余处理程序,继续查找匹配的路由。匹配时会同时考虑请求方法和路径。只有在使用app.METHOD()或router.METHOD()定义的中间件函数中,这种用法才有效。

    • next(new Error(value))会将控制权传递给下一个错误处理中间件。

    • 如果当前正在运行的中间件没有结束请求-响应周期,那么就需要在回调函数中使用next参数来将控制权传递给下一个中间件。否则,客户端的请求将会处于待处理状态。

    Express.js中的中间件分类

    Express中间件可以分为五大主要类别:

    • 应用层中间件

    • 路由器层中间件

    • 错误处理中间件

    • 内置中间件

    • 第三方中间件

    • 这些分类说明了中间件的安装位置、其行为方式或其来源。

      应用层中间件

      这种类型的中间件是直接附加到Express应用程序实例(app)上的。

      示例:

      import express from "express";
      const app = express();
      
      app.get("/profile", (req, res, next) => {
        console.log("Hi, there!");
        next();
      });
      

      路由器层中间件

      这种中间件是附加到Express Router实例(router)上的。它的运作方式与应用层中间件类似,但它是安装在路由器上而不是应用程序上。

      示例:

      import express from "express";
      const router = express.Router();
      
      router.get("/profile", (req, res, next) => {
        console.log("Hi, there!");
        next();
      });
      

      错误处理中间件

      这种中间件用于处理请求处理过程中出现的错误,其函数签名包含四个参数。

      import express from "express";
      const app = express();
      
      app.use((err, req, res, next) => {
        console.error(err);
        res.status(500).send("Error 500: 遇到了意外错误");
      });
      

      注意:

      • 即使某些参数没有被使用,这四个参数也必须全部存在。否则,Express无法识别该函数为错误处理中间件。Express是根据声明的参数数量来识别这类中间件的,而不是参数的名字。

      • 应该将错误处理中间件定义在那些需要它来处理的路由和中间件之后。这样的安排可以确保它能处理来自前面中间件函数的错误请求。

      内置中间件

      这些中间件函数是Express自带的功能,默认即可使用,无需额外安装任何包。

      示例:

      import express from "express";
      
      express.static("public");
      express.json();
      express.urlencoded();
      

      第三方中间件

      这些中间件函数是由Express社区其他开发者开发并分享的第三方包提供的。例如cors、morgan和helmet等。

      import express from "express";
      import cors from "cors";
      
      const app = express();
      app.use(cors());
      

      注意:Express中的中间件分类并非互斥的

      示例1:

      import express from "express";
      
      const app = express();
      app.use(express.json());
      

      上面代码中的express.json()中间件同时属于:

      • 应用层中间件,因为它被添加到了app实例上

      • 内置中间件,因为Express本身提供了这个功能

      示例2:

      import express from "express";
      import cors from "cors";
      
      const router = express.Router();
      router.use(cors());
      

      上面代码中的cors()中间件同时属于:

      • 路由器层中间件,因为它被添加到了router实例上

      • 第三方中间件,因为它是通过外部包提供的

      你可能会问上面示例中的express.Router()方法是什么?让我们来了解一下。

      什么是express.router()方法?

      express.router()方法用于创建一个路由器对象,该对象可用于组合相关的路由和中间件。你可以使用app.use()或router.use()将这个路由器对象添加到Express应用中或其他路由器上。

      为什么Express Router被称为‘迷你应用’?

      如上图所示,路由器常被称为“迷你应用”,因为它们能够提供完整的中间件和路由系统。通过这种方式,我们可以将相关的路由集中在一起,并将其挂载到主Express应用上。例如,假设你的Express应用需要处理以下类别的路由和中间件:

      • 首页: 用于处理与首页相关的路由和中间件。

      • 书籍: 用于处理与书籍相关的路由和中间件。

      • 视频: 用于处理与视频相关的路由和中间件。

      在这种情况下,你可以使用express.Router()方法来将相关的路由和中间件组合在一起。这样可以使代码结构更加清晰,便于管理和重复使用。

      虽然你可以在一个文件中创建所有的路由器和应用实例,但开发者通常会将其分开存储在不同的文件中,以提升代码的组织性和可维护性。因此,我们也应该采用这种做法,为每一类路由和中间件分别创建单独的文件。

      为路由器创建目录

      在项目的根目录下创建一个routes文件夹,用来存放所有的应用路由。

      mkdir routes
      

      注意:你可以随意给这个文件夹起名,但routes是一个常见的选择,因为这个名字正好表示了该文件夹中存放的是定义应用路由的文件。express.Router()方法只是Express提供的一个用于管理这些路由的工具而已。

      创建路由文件

      路由文件是一种模块,在其中你可以定义相关的路由,并导出一个Express路由器对象。每个文件通常对应着某个特定的资源或功能,比如用户、书籍或认证相关的内容。

      在routes文件夹中创建相应的路由文件。

      touch routes/index.js routes/books.js routes/videos.js
      

      打开books.js文件,使用express.Router()实例来组合所有与书籍相关的路由和中间件。

      routes/books.js

      // 从Express中导入Router函数
      import { Router } from "express";
      
      // 创建一个新的Express路由器实例
      const bookRouter = Router();
      
      // 定义专门针对这个路由器实例的中间件
      bookRouter.use((req, res, next) => {
        console.log("访问了书籍路由:", Date.now());
        next();
      });
      
      // 定义针对这个路由器实例的路由:
      bookRouter.get("/", (req, res) => {
        res.send("

      所有书籍列表

      "); }); bookRouter.get(":id", (req, res) => { res.type("text/plain").send(`书籍${req.params.id}的详细信息`); }); bookRouter.get(":id/draft", (req, res) => { // 在将ID插入HTML文本之前对其进行转义处理 const bookId = req.params.id .replaceAll("&", "&amp;") .replaceAll("<", "<") .replaceAll(">", ">" res.send("

      书籍${bookId}的草稿版本

      "); }); export { bookRouter };

      上述代码的作用如下:

      • 创建了一个bookRouter实例,以便使用Express提供的各种API方法,如.get()、.post()、.use()和.route()。

      • 使用Express的router.use()方法将中间件添加到bookRouter实例上。

      • 使用Express的router.get()方法来指定服务器如何处理HTTP GET请求。

      • 使用Express的res.send()方法发送HTTP响应,并根据数据类型自动设置相应的头部信息。其中,/:id路由会将ID转义后才能显示出来。

      • 使用export语句使得这个路由器对象可以在应用程序的任何需要使用它的地方被导入。

      是时候练习如何使用路由器了

      现在就是你检验自己所学关于Express路由器知识的好机会。

      对于这个练习,你需要为项目的首页以及与视频相关的路由和中间件创建迷你应用。将它们分别从`routes/index.js`文件中导出为`indexRouter`,从`routes/videos.js`文件中导出为`videoRouter`,以便在后续步骤中使用。

      在继续之前,请先自己尝试操作一下。如果需要帮助,可以参考`books.js`文件。通过练习,你会更好地理解这些内容。

      完成这个练习后,就可以开始设置主Express应用了。

      更新主Express应用

      打开你项目的`server.js`文件,将刚刚创建的路由器添加到主Express应用中。

      server.js

      
      // 导入Express模块
      import express from "express";
      
      // 从相应的文件中导入路由器
      import { indexRouter } from "./routes/index.js";
      import { bookRouter } from "./routes/books.js";
      import { videoRouter } from "./routes/videos.js";
      
      // 创建一个新的Express应用实例
      const app = express();
      
      // 指定主机和端口来接收请求
      const host = "localhost";
      const port = 3000;
      
      // 将路由器挂载到特定的路径上:
      // 将indexRouter挂载到/
      app.use("/", indexRouter);
      
      // 将bookRouter挂载到/books及其子路径上
      app.use("/books", bookRouter);
      
      // 将videoRouter挂载到/videos及其子路径上
      app.use("/videos", videoRouter);
      
      // 在指定的端口和主机上运行服务器
      app.listen(port, host, () => {
        console.log(`服务器正在http://${host}:
      ${port}上运行!`);
      });
      

      上述代码的作用如下:

      • 创建一个Express应用实例,以便使用诸如路由器和中间件这样的Express功能,从而简化Node.js中的HTTP请求处理流程。

      • 使用Express的`app.use()`方法,将这些迷你应用添加到主Express应用中。

      • 使用Express的`app.listen()`方法,在指定的端口上启动Node.js HTTP服务器,并在服务器开始接收客户端请求时执行相应的回调函数。

      注意:`app.use()`方法中指定的路径被视为路由器的基础路径或挂载路径。在路由器中定义的路由会相对于这个路径进行匹配。例如:

      • `bookRouter`中的`/`路径会匹配对 `/books` 的请求。

      • `bookRouter`中的`/:id`路径会匹配对 `/books/:id` 的请求。

      • `bookRouter`中的`/:id/draft`路径会匹配对 `/books/:id/draft` 的请求。

      运行你的Express.js应用

      一旦完成了路由器的配置,就可以通过使用Node.js启动服务器文件来运行你的应用了。

      node --watch server.js

      然后,检查你的应用程序是否在指定的端点正常运行。例如,访问 http://localhost:3000/books。

      提示:在 Windows、macOS 或 Linux 系统上,可以使用 Ctrl + C 来停止正在运行的进程。

      关于 Express 路由器需要了解的重要事项

      在使用 Express 路由器时,有一些重要的事项需要记住。

      默认情况下,路由器不会继承父路由的参数

      默认情况下,Express 不会将某个路由的路径参数传递给该路由内部的子路由。

      示例: // 添加 Express 模块 import express from "express"; // 创建一个新的 Express 应用程序实例 const app = express(); // 创建一个新的 Express 路由器实例 const bookRouter = express.Router(); // 定义一个子路由 bookRouter.get("/books/:bookId", (req, res) => { const topic = req.params.topic; // 默认值为 undefined const bookId = req.params.bookId; res.json({ topic, bookId }); }); // 定义一个父路由 app.use("/codesweetly/:topic", bookRouter); // 在指定的端口和主机上运行服务器 const server = app.listen(3000, "127.0.0.1", () => { const { address, port } = server.address(); console.log(`服务器正在 http://${address}: ${port} 上运行!`); });

      如果你运行上述示例中的 Web 服务器,并在浏览器中输入以下 URL:

      http://127.0.0.1:3000/codesweetly/CSS/books/91827

      那么 req.params.topic 的值将会是 undefined,因为默认情况下子路由无法访问父路由的路径参数。因此,服务器只会向客户端返回一个包含 "bookId":"91827" 的响应。

      如果想让子路由能够访问其父路由的路径参数,在创建路由器时需要将 mergeParams 设置为 true。

      示例: // 添加 Express 模块 import express from "express"; // 创建一个新的 Express 应用程序实例 const app = express(); // 创建一个新的 Express 路由器实例,并将 mergeParams 设置为 true const bookRouter = express.Router({ mergeParams: true }); // 定义一个子路由 bookRouter.get("/books/:bookId", (req, res) => { const topic = req.params.topic; // 在下面的 URL 中,这个参数的值为 "CSS" const bookId = req.params.bookId; res.json({ topic, bookId }); }); // 定义一个父路由 app.use("/codesweetly/:topic", bookRouter); // 在指定的端口和主机上运行服务器 const server = app.listen(3000, "127.0.0.1", () => { const { address, port } = server.address(); console.log(`服务器正在 http://${address}: ${port} 上运行!`); });

      如果你运行上述示例中的 Web 服务器,并在浏览器中输入以下 URL:

      http://127.0.0.1:3000/codesweetly/CSS/books/91827

      req.params.topic 这个参数的值将会是 CSS,因为 mergeParams: true 允许子路由访问父路由的路径参数。因此,服务器会向客户端发送一个 {"topic":"CSS","bookId":"91827"} 的响应。

      提示:

      • 代码行 const { address, port } = server.address() 使用了 对象解构 的方式,从 server.address() 对象中获取 address 和 port 这两个值。

      • app.listen() 方法会返回一个原生 Node.js 的 http.Server 实例,这样就可以使用诸如 .address()、.on() 和 .maxHeadersCount 这样的服务器接口了。

      • 127.0.0.1 是一个 IPv4 回环地址。localhost 可能会解析为 127.0.0.1,或者 IPv6 的回环地址 ::1。在这些示例中我们使用 http://127.0.0.1:3000,是因为服务器明确设置在了这个 IPv4 地址上监听请求。

      路由器可以被安装在其他路由器上

      你可以将一个 Express 路由器连接到另一个 Express 路由器上。

      示例:

      // 导入 Express 模块
      import express from "express";
      
      // 创建一个新的 Express 应用实例
      const app = express();
      
      // 创建两个 Express 路由器实例
      const htmlRouter = express.Router();
      const bookRouter = express.router();
      
      // 定义一个子路由
      bookRouter.get(":bookId", (req, res) => {
        res
          .type("text/plain")
          .send(`ID 为 ${req.params.bookId} 的 HTML 图书的详细信息`);
      });
      
      // 将 bookRouter 映射到 htmlRouter 的 /books 路由上
      htmlRouter.use("/books", bookRouter);
      
      // 将 htmlRouter 映射到 /html 及其所有子路径上
      app.use("/html", htmlRouter);
      
      // 在指定的端口和主机上运行服务器
      const server = app.listen(3000, "127.0.0.1", () => {
        const { address, port } = server.address();
        console.log(`服务器正在地址 http://${address}:
      ${port} 上运行!`);
      });
      

      bookRouter 中的 GET /:bookId 路由会匹配到 GET /html/books/:bookId 这个请求,因为在这个示例中,bookRouter 是被连接到 htmlRouter 上的。

      因此,如果你运行上面的示例中的 Web 服务器,并在浏览器中输入以下 URL:

      http://127.0.0.1:3000/html/books/91827

      服务器会向客户端发送一个 “ID 为 91827 的 HTML 图书的详细信息” 的响应。

      现在你已经了解了什么是路由器,接下来我们就来讨论如何使用视图来定义你的 Express.js 应用程序返回给用户的网页的内容和结构吧。

      在 Express.js 中,视图是什么?

      视图是一种模板或文档,它用于定义Express.js应用程序向用户返回的网页的内容与结构。这种视图既可以是一个静态的HTML文件,也可以是由模板引擎在运行时渲染成HTML格式的动态模板。更多相关信息请参见运行时。

      在本指南中,我们将介绍两种类型的视图:

      • 静态视图

      • 动态视图

      Express.js中的静态视图是什么?

      静态视图包含服务器直接发送给浏览器的最终HTML内容。例如,让我们配置你的Express.js应用程序来提供四种静态视图。

      创建静态视图

      在项目的根目录下创建`index.html`、`about.html`、`contact.html`和`404.html`这些文件。

      touch index.html about.html contact.html 404.html

      打开每个视图文件,然后添加以下HTML内容:

      index.html(首页)
      <!doctype html>
      <html lang="en">
        
          Express.js中的静态视图 | CodeSweetly教程
        
        
          

      欢迎使用静态视图指南

      网站页面:

      about.html(关于我们页面)
      <!doctype html>
      <html lang="en">
        
          
          
          关于我们 | Express.js中的静态视图
        
        
          

      关于我们

      Lorem ipsum dolor sit amet consectetur adipisicing elit. Libero soluta voluptas reprehenderit minus veniam! Corrupti a esse quidem nostrum harum, explicabo tempore tempora aut, sint et voluptatem magni ea vel?

      contact.html(联系我们页面)
      <!doctype html>
      <html lang="en">
        
          
          
          联系我们 | Express.js中的静态视图
        
        
          

      联系我们

      404.html(错误页面)
      <!doctype html>
      <html lang="en">
        404 - 未找到页面 | Express.js中的静态视图未找到页面
          
      抱歉,我们找不到该页面。
      </html>>

      接下来,配置您的项目以处理浏览器的连接请求。

      配置项目的网络服务器以提供静态视图

      打开项目的server.js模块,配置网络服务器以便在收到请求时能够返回相应的静态HTML页面。

      // 添加所需的模块
      import express from "express";
      import { dirname, join } from "node:path";
      import { fileURLToPath } from "node:url";
      
      // 创建一个新的Express应用程序实例
      const app = express();
      
      // 指定接收请求的主机名和端口
      const host = "localhost";
      const port = 3000;
      
      // 获取当前文件路径所在的目录名称
      const __dirname = dirname(fileURLToPath(import.meta.url));
      
      // 当收到'GET /'请求时,返回index.html静态页面
      app.get("/", (req, res) => res.sendFile(join(__dirname, "index.html"));
      
      // 当收到'GET /about'请求时,返回about.html静态页面
      app.get("/about", (req, res) => {
        res файlSend(join(__dirname, "about.html"));
      });
      
      // 当收到'GET /contact'请求时,返回contact.html静态页面
      app.get("/contact", (req, res) => {
        res файlSendJOIN(__dirname, "contact.html"));
      });
      
      // 当收到未匹配的请求时,返回404.html静态页面
      app.use((req, res) => {
        res.status(404). файlSend(join(__dirname, "404.html"));
      });
      
      // 在指定的端口和主机名上运行服务器
      app.listen(port, host, () => {
        console.log(`服务器正在http://${host}:
      ${port}上运行`);
      });
      

      上述代码主要完成了以下几项工作:

      • 创建了一个Express应用程序实例,以便使用Express提供的API(如路由器和中间件)来简化Node.js中的HTTP请求处理流程。

      • 获取了当前文件路径所在的目录名称。

        • dirname()方法用于获取路径所在的目录名称。

        • fileURLToPath()方法用于将URL转换为有效的路径字符串。

        • import.meta.url方法用于获取当前文件的绝对URL地址。

      • 定义了路由规则,以便接收并响应客户端的请求。

        • app表示一个Express应用程序实例。

        • app.get()方法用于指定服务器如何处理针对特定路径的HTTP GET请求。

        • res файlSend()方法用于将服务器上的文件发送给客户端。

        • join()方法使用当前操作系统的路径分隔符,将多个路径段合并成一条规范的路径字符串。

        • app.use()方法允许为所有HTTP请求方法和路径设置处理函数。如果指定了路径参数,app.use()会将其视为匹配路由的前缀。例如,/book可以匹配/book、/book/dashboard以及/book/dashboard/author/202605等路径。

        • res.status(404)方法用于将HTTP状态码设置为404,表示请求没有得到任何响应。

      • 使用Express的app.listen()方法,在指定的端口上启动Node.js HTTP服务器,并在服务器开始接收客户端请求时执行回调函数。

      运行你的Express.js应用程序

      设置好服务器后,使用Node.js来运行它。

      node --watch server.js

      之后,可以在http://localhost:3000地址查看你的应用是否正在正常运行。

      提示:在Windows、macOS或Linux系统中,可以使用Ctrl + C来停止应用程序的运行。

      为了保持代码组织的整洁性,应该将所有的静态资源文件放在public目录中。这是一种常见的做法。

      创建用于存放静态资源的目录

      在项目的根目录下创建一个public目录,用来存储所有的静态资源文件。

      mkdir public

      注意:你可以随意给这个文件夹起名字,但public是一个常见的选择,因为这个名字表明该文件夹中的文件(HTML、CSS、客户端JavaScript代码、图片等)是专门为提供给客户端使用的。仅仅通过文件夹的名字,并不能使其中的文件能够被访问;必须配置服务器才能让这些文件可以被客户端获取。因此,请不要在public目录中存放敏感信息。

      将所有静态资源文件移到public目录中

      mv index.html about.html contact.html 404.html public/

      更新服务器配置,以便从public目录中获取静态资源文件

      server.js

      import express from "express";
      import { dirname, join } from "node:path";
      import { fileURLToPath } from "node:url";
      
      const app = express();
      const host = "localhost";
      const port = 3000;
      const __dirname = dirname(fileURLToPath(import.meta.url));
      
      // 从'public'目录中提供静态资源文件
      app.use(express.static(join(__dirname, "public"));
      
      // 当有人访问'GET /'时,返回index.html文件
      app.get("/", (req, res) => {
        res.sendFile.join(__dirname, "/public/index.html"));
      });
      
      // 当有人访问'GET /about'时,返回about.html文件
      app.get("/about", (req, res) => {
        res файlSend(join(__dirname, "/public/about.html"));
      });
      
      // 当有人访问'GET /contact'时,返回contact.html文件
      app.get("/contact", (req, res) => {
        res файlSend.join(__dirname, "/public/contact.html"));
      });
      
      // 当有匹配不到的请求时,返回404.html文件
      app.use((req, res) => {
        res.status(404). файlSend(join(__dirname, "/public/404.html"));
      });
      
      // 在指定的端口和主机名上运行服务器
      app.listen(port, host, () => {
        console.log(`服务器正在http://${host}:
      ${port}地址上运行`);
      });
      

      注意:代码中的app.use(express.static(join(__dirname, 'public')))这一行告诉Express,在处理静态资源请求时,首先要检查public目录。如果在该目录中找到了所需的文件,就会立即将其提供给浏览器,从而结束请求-响应流程。

      示例:
      • 如果用户请求访问 localhost:3000/about.html,express.static 会在 public 文件夹中查找 about.html 文件。如果找到该文件,Express 会直接将其发送给客户端,而不会执行后续的路由处理程序。

      • 如果用户请求访问 localhost:3000/about,express.static 会在 public 文件夹中查找名为 about 的文件(不带扩展名)。如果找到的是一个目录,Express 会将其重定向到 /about/ 路由,并在该路径下默认查找 index.html 文件。根据本指南中的示例设置,public 文件夹中既没有 about.html 文件,也没有 about 目录,因此 Express 会使用自定义的 GET /about 路由来处理这个请求。

      现在让我们来讨论动态视图的概念。

      Express.js中的动态视图是什么?

      动态视图包含占位符,模板引擎(如 EJS)会在运行时用实际数据替换这些占位符。

      Express.js 支持多种模板引擎,包括 EJS、Pug 和 Handlebars。我们以 EJS 为例来说明动态视图的原理。

      什么是 EJS?

      EJS(嵌入式 JavaScript 模板语言)是一种模板引擎,它允许你在 HTML 模板中嵌入 JavaScript 代码,从而生成动态的 HTML 内容。

      当服务器渲染模板时,EJS 引擎会执行嵌入的 JavaScript 代码,并生成最终的 HTML 结果发送给客户端。

      举个例子,来看以下代码。在运行这个示例之前,请先使用 npm install ejs 安装 EJS:

      // 导入 Express 和 EJS 模块
      import express from "express";
      import ejs from "ejs";
      
      // 创建一个新的 Express 应用实例
      const app = express();
      
      // 指定服务器主机和端口
      const host = "localhost";
      const port = 8000;
      
      // 定义数据对象
      const friendsArray = ["Sarah", "Abraham", "Mary"];
      const replacementObject = { friends: friendsArray };
      
      // 使用反引号定义包含 JavaScript 代码的模板字符串
      const templateString = `
        

      Hello, <%= friends.join(", "); %>>!<\/h1>

      欢迎访问 CodeSweetly。

      `; // 将模板字符串编译成 HTML 并进行渲染 const html = ejs.render(templateString, replacementObject); // 使用生成的动态视图响应 'GET /' 请求 app.get("/", (req, res) => { res.send(html); }); // 在指定的端口和主机上运行服务器 app.listen(port, host, () => { console.log(`服务器正在 http://${host}:$port 上运行!`); });

      上述代码告诉 Express:当用户请求访问 / 路径时,要将渲染后的 HTML 内容发送给客户端。

      在运行时,EJS 会使用传递过来的数组 friends 来计算 friends.join(", ") 的结果,并将这个结果插入到 HTML 中。因此,每次请求都会得到相同的渲染结果。

      什么是EJS模板标签?

      EJS模板标签是使用<% ... %>语法编写的特殊标签。它们允许将JavaScript代码和数值嵌入到EJS视图模板中。

      当EJS渲染模板时,scriptlet标签会执行JavaScript代码,output标签会插入数值,而comment标签则会被忽略。

      例如,下面的<% %>标签允许你将一个JavaScript变量嵌入到视图模板中。

      关于<%= friend %>,我的特别朋友

      注意:像<% %>这样的output标签必须包含有效的JavaScript表达式。Scriptlet标签可以包含JavaScript语句和控制流结构,而comment标签则用于编写注释。

      示例:

      • <%

        I love CodeSweetly

        %>会引发错误,因为

        I love CodeSweetly

        是HTML代码,而不是JavaScript。

      • <% "

        I love CodeSweetly

        " %>是有效的,因为"

        I love CodeSweetly

        "
        被当作JavaScript字符串类型嵌入到了模板中。

      EJS模板标签的类型

      这里介绍了九种常见的EJS标签类型:

      提示:请将JavaScript代码用EJS标签括起来,以便将其与周围的模板内容区分开来。一对标签可以包含多行JavaScript代码。

      闭合标签(%>)

      使用闭合标签来结束一个EJS标签。该标签本身没有特殊的功能。

      Scriptlet标签(<%>)

      使用scriptlet标签来执行JavaScript代码,但不会在生成的HTML中显示任何结果。这种标签常用于控制流结构,比如if、for和forEach等逻辑。

        <% friends.forEach(function () { %>
      • 我的朋友
      • <% }) %>

      上面的代码示例将控制流结构用<% %>括起来,告诉EJS这段JavaScript代码仅用于逻辑处理,不应被渲染到生成的HTML中。

      转义输出标签(<%>)

      使用转义输出标签来计算JavaScript表达式的结果,并将结果插入到生成的HTML中,同时会转义HTML中的特殊字符。这种标签有助于防止HTML注入攻击。

      <% 100 + 200 %>

      上面的代码示例将算术表达式用<% %>括起来,让EJS将计算结果渲染到生成的HTML中。

      注意: 如果表达式的计算结果是一个包含HTML代码的字符串,<%= %>这个标签会自动转义其中的HTML语法,这样浏览器就会将其显示为普通文本。例如,<%= "<h1>关于CodeSweetly</h1>" %>这段代码会使得

      关于CodeSweetly

      这部分内容以文本形式显示,而不会被浏览器解析成真正的

      标签。

      未转义的输出标签(<%-)

      使用未转义的标签来执行JavaScript表达式,并将结果直接输出到生成的HTML代码中,而不会对其中的HTML字符进行转义处理。

      <%- "<h1>>关于CodeSweetly</h1>>" %>

      上述示例中,<%- %>这个标签被用来包围包含

      标签的字符串,因此浏览器会将其解析为正常的HTML代码。只有在对可信的HTML内容进行处理时,才应该使用未转义的输出标签。

      字面意义上的开头标签(<%%)

      使用这种标签可以直接输出<%这个序列,而不会被EJS引擎解析为标签。当你希望在生成的HTML代码中显示EJS的语法结构时,这种方法非常有用。

      <>%% "

      姓名: Oluwatobi

      " %>

      上述示例中,<%%这个标签被直接输出出来,而不会被当作EJS代码进行解析。因此,最终生成的HTML代码会是:

      <>% "
      

      姓名: Oluwatobi

      "

      其中的HTML标签都没有被转义,因此这里显示的是原始的代码结构,而不是浏览器最终呈现的页面效果。

      <>ul> <:% friends.forEach(function (friend) { %>
    • <%= friend %></li> <:% }) %>

    在上述示例中,<%= -%>这个标签被用来包围变量friends,这样EJS引擎就会在输出时自动去除关闭标签后面的换行符。

    提示:这种去除换行符的结束标签也被称为“换行符吸收标签”。

    <>ul> <:%_ friends.forEach(function (friend) { %>
  • <%= friend %></li> <:%_ }) %>

    在上述示例中,<%@_ %>这个标签被用来包围控制流语句,这样EJS引擎就会在输出时自动去除模板标签前面的缩进格式。

    <ul> <% friends.forEach(function (friend) { _%> <li><%= friend %></li> <% }) _%> </ul>

    上面的代码示例使用`<% _%>`将控制流语法括起来,这样EJS就会在每个关闭标签后删除后面的换行符。而下一行的缩进格式则不会被改变。

    注释标签(<%#)

    使用注释标签可以添加EJS注释,这些注释在渲染过程中会被忽略,因此也不会出现在生成的HTML中。

    <ul>
      <%# 遍历friends数组 %>
      <% friends.forEach(function (friend) { _%>
      <li><%= friend %></li>
      <% }) _%>
    </ul>
    

    上面的代码示例将注释内容用`<%# %>`括起来。

    提示:EJS允许你组合某些标签。例如,以下几种组合都是可行的:

    • <%_ _%>:将“吸走空白字符”的脚本片段与结束标签结合起来,可以删除同一行上相邻的空格和制表符;如果后面有换行符,也会被删除。

    • <% -%>:将控制流结构与“吸走换行符”的功能结合起来使用。

    • <%= -%>:将转义标签与“吸走换行符”的功能结合起来使用。

    如何在Express项目中使用EJS

    接下来的内容将指导你如何在自己的Express.js项目中使用EJS。

    安装EJS

    首先,需要在你的Express项目中安装EJS。

    npm install ejs@6.0.1
    

    接下来,打开项目的`server.js`文件,配置Web服务器以便在收到请求时能够返回相应的动态生成的HTML页面。

    server.js

    // 加入Express和EJS模块
    import express from "express";
    import ejs from "ejs";
    
    // 创建一个新的Express应用实例
    const app = express();
    
    // 指定接收请求的主机和端口
    const host = "localhost";
    const port = 8000;
    
    // 定义数据对象
    const friendsData = {
      bestFriend: "Sarah",
      codingFriend: "Abraham",
      dreamFriend: "Mary",
    };
    const replacementObject = { friends: friendsData };
    
    // 使用反引号定义包含JavaScript代码的模板字符串
    const templateString = `
    <html>
      朋友列表
    • <%# 遍历friends对象 %> <% for (const eachFriend in friends) { -%>
    • <%= friends[eachFriend] %>> 是我的<% eachFriend %>>
    • <% } -%>
    </html> `; // 将模板字符串编译成HTML页面 const htmlData = ejs.render(templateString, replacementObject); // 使用生成的动态页面响应‘GET /’请求 app.get("/", (req, res) => res.send(htmlData)); // 在指定的端口和主机上运行服务器 app.listen(port, host, () => { console.log(`服务器正在http://${host}: ${port}上运行!`); });

    以上代码片段主要完成了以下几项工作:

    • 创建了一个 Express 应用程序实例,以便使用 Express 提供的各种 API,比如路由器和中间件,这些工具能够简化 Node.js 中的 HTTP 请求处理流程。

    • 在脚本启动时,会一次性渲染模板字符串,从而生成服务器发送给所有客户的 HTML 内容。

    • 设置了相应的路由规则,用于接收并响应客户端的请求。

    • 使用 Express 的 `app.listen()` 方法,在指定的端口上启动 Node.js HTTP 服务器,并在服务器开始监听客户端请求时执行相应的回调函数。

    运行 Express 应用程序

    完成服务器配置后,使用 Node.js 启动它。

    node --watch server.js

    之后,可以在 http://localhost:8000 这个地址上查看你的应用程序是否正在运行。

    提示:在 Windows、macOS 或 Linux 系统中,可以使用 Ctrl + C 来停止程序的运行。

    为了保持代码组织的整洁性,建议将所有的视图模板文件放在一个名为 views 的目录中。这是一种常见的做法。

    创建用于存放视图模板的目录

    在项目根目录下创建一个名为 views 的目录,用来存储所有的视图模板文件。

    mkdir views
    创建索引视图模板文件
    touch views/index.ejs

    提示:EJS 模板文件的扩展名是 .ejs。

    打开这个文件,将之前放在 server.js 文件中的 HTML 模板内容复制到其中。

    views/index.ejs

    <html>
      <body>
        <h1>朋友列表</h1>
        
      <%# 遍历 friends 对象中的元素 %> <%; for (const eachFriend in friends) { -%> <li><%= friends[eachFriend] %>> 是我的朋友<</li> <% } -%>
    /body>> </html>
    更新服务器配置,使其从 views 目录中读取视图模板文件

    server.js

    import express from "express";
    import { dirname, join } from "node:path";
    import { fileURLToPath } from "node:url";
    
    const app = express();
    const host = "localhost";
    const port = 8000;
    const __dirname = dirname(fileURLToPath(import.meta.url));
    
    // 指定应用程序的视图模板目录
    app.set("views", join(__dirname, "views"));
    
    // 指定应用程序使用的视图引擎
    app.set("view engine", "ejs");
    
    // 定义数据对象
    const friendsData = {
      bestFriend: "Sarah",
      codingFriend: "Abraham",
      dreamFriend: "Mary",
    };
    const replacementObject = { friends: friendsData };
    
    // 将索引视图模板编译成 HTML,并将其作为对 ‘GET /’ 请求的响应返回给客户端
    app.get("/", (req, res) => {
      res.render("index", replacementObject);
    });
    
    // 在指定的端口和主机名上运行服务器
    app.listen(port, host, () => {
      console.log(`服务器正在 ${host}:${port} 上运行。`);
    });
    
    上述代码片段使用了 Express 的 render() 方法,根据 index.ejs 模板文件生成 HTML 内容。 注意:
    • 如果省略了 app.set("view engine", "ejs") 这一行代码,那么 render() 方法的模板参数必须包含类似的文件扩展名。
    app.get("/", (req, res) => {
      res.render("index.ejs", replacementObject);
    });
    
    • Express 并没有默认的模板引擎。它同时也支持 Pug(以前称为 Jade)、EJS、Mustache 和 Handlebars 等模板引擎。
    • app.set() 是用于向应用程序的配置中添加键值对的方法。你可以使用 app.get() 来获取这些配置的值,例如:
    app.set("my name", "Oluwatobi");
    
    const bio = app.get("my name");
    
    console.log(bio); // 输出:"Oluwatobi"
    

    如何创建可重用的 EJS 模板(部分模板)

    EJS 提供了 include() 方法,用于将一个模板嵌套到另一个模板中。

    提示:在 EJS 中,部分模板就是可以被重复使用的模板。

    include() 方法的语法

    include() 方法接受两个参数。其语法如下:

    <%- include("path/to/partial", data) %>
    
    • "path/to/partial":(必填)指向可重用模板的路径,该路径是相对于当前文件而言的。例如,如果当前文件位于 "./views/index.ejs",而部分模板位于 "./views/partialsfooter.ejs",那么 include() 方法的路径参数应为 "partials/footer"。

    • data:(可选)一个对象,其中包含要传递给部分模板的属性。

    如何在视图模板中嵌入部分模板

    请看以下服务器文件示例:

    server.js

    import express from "express";
    import { dirname, join } from "node:path";
    import { fileURLToPath } from "node:url";
    
    const app = express();
    const host = "localhost";
    const port = 8080;
    const __dirname = dirname(fileURLToPath(import.meta.url));
    
    app.set("views", join(__dirname, "views"));
    app.set("view engine", "ejs");
    
    const friendsData = {
      bestFriend: "Sarah",
      codingFriend: "Abraham",
      dreamFriend: "Mary",
    };
    const replacementObject = { friends: friendsData };
    
    app.get("/", (req, res) => {
      res.render("index", replacementObject);
    });
    
    app.listen(port, host, () => {
      console.log(`服务器正在运行,地址为 http://${host}:
    ${port}`);
    });
    

    当用户向 "/" 路径发送 GET 请求时,上述代码片段会渲染 index 模板。下面是 index.ejs 模板文件的内容。

    views/index.ejs

    <html>
      朋友列表
        
      < <%# 遍历friends对象 %> < <% for (const eachFriend in friends) { -%>
    • <%= friends[eachFriend] %>> 是我的<%= eachFriend %><>/li> < <% } -%> <\/ul> </html>

    如果你需要将

  • 元素重复使用在多个模板中,可以将其提取到一个单独的文件中,然后通过include()方法根据需要在任何模板中引入它。具体操作方法如下:

    创建一个部分文件目录

    在views文件夹中创建一个partials目录,用来存放所有可重复使用的视图模板。

    mkdir views/partials
    创建一个部分视图模板

    为

  • 元素创建一个部分视图模板。

    touch views/partials/friendLi.ejs

    打开该文件,然后添加

  • 元素。

    views/partials/friendLi.ejs

  • <%= friend %>> 是我的<%= friendType %><>/li>
  • 更新主视图模板

    打开index.ejs文件,使用include()方法引入friendLi.ejs部分视图模板。

    views/index.ejs

    朋友列表
        
      < <%# 遍历friends对象 %> < <% for (const eachFriend in friends) { -%> <%@- include("partials/friendLi", { friend: friends[eachFriend], friendType: eachFriend }) %> < <% } -%> <\/ul>> </html>

    通过使用include()方法引入

  • 元素,就可以在多个模板中重复使用它,而不会将其限制在index.ejs视图模板中。

    从根目录运行服务器
    node --watch server.js

    然后,查看你的应用程序是否在http://localhost:8080地址上正常运行。

    注意:虽然上面的示例是使用简单的

  • 元素来演示EJS部分文件的用法,但你可以利用同样的原理,在多个视图模板中重复使用应用程序的导航栏、页脚等其他组件。

    概述

    在这本手册中,我们介绍了开始使用Node.js和Express.js构建应用程序所需的核心概念。我们探讨了Node.js的工作原理、它的模块支持机制,以及如何创建HTTP服务器。同时,我们也了解了Express.js是如何通过路由、路由器及视图功能简化服务器端开发的。

    无论您是在考虑一个小型个人项目,还是为更多用户开发一款全栈应用程序,现在您都已经拥有了使用 Node.js 和 Express.js 进行开发的坚实基础。

    感谢您的阅读!

    深入了解 Node.js 和 Express.js

    通过这本手册,您已经初步了解了我的那本《Node.js 和 Express.js 入门指南》。

    无论您是第一次学习后端开发,还是希望将自己的 JavaScript 技能应用于后端开发领域,又或者只是想获得关于 Node.js 和 Express.js 的实用入门知识,这本书都能帮助您在已有知识的基础上进一步学习,掌握创建、测试及部署完整应用程序所需的各项技能。

    通过实际操作示例和详细讲解,您将更深入地了解 Node.js 和 Express.js,这些内容涵盖文件管理、文件上传、表单处理、会话管理、前端与后端的通信、测试流程、TypeScript 的使用以及应用程序的部署方法等主题。

    使用 Node.js & Express.js 开发网站:一本适合初学者的实用指南,涵盖开发、测试与部署流程

  • 相关文章

    技术实践

    如何将Jekyll博客主题移植到Python环境中:实际操作中的经验与教训

    几年来,我一直在使用一个采用 tufte-jekyll 样式设计的博客,正是这个经历让我发现了Edward Tufte所提出的布局理念。 Edward Tufte 因在数据可视化与信息设计领域的贡献而闻名,他是高数据密度设计的坚定支持者,同时也极力反对使用那些毫无意义的视觉元素。 tufte-css (以及它的许多衍生版本)为网页设计带来了诸多优势:充足的空白空间、适合阅读的排版格式,还有用于提供补充信息的 侧边注释 (而非干扰用户体验的弹出窗口)。 除了那些与写作无关的部分外,我对 tufe-jekyll 博客的设计几乎毫无意见。这个基于 Jekyll 框架、使用 Ruby 语言开发的版本,

    阅读全文
    技术实践

    如何使用Python和依赖关系图来检测公共数据集中隐藏的目标泄露现象

    不久前,我给一个机器学习模型提供了来自公共CDC数据集的五列数据,让它尝试预测同一文件中的第六列数据。该模型的R²值为0.998,这个数值已经非常接近完美了。 虽然这个结果看起来很成功,但实际上该模型对现实世界的认知几乎没有任何提升——因为CDC本身就是根据其他五列数据计算出了第六列数据的,所以该模型实际上只是机械地应用了CDC的计算方法而已。 数据科学家将这种问题称为 目标信息泄露 ,当输入给模型的数据本身就已经包含了答案的某些信息时,就会发生这种情况。 在公共数据中,这类问题很容易被隐藏起来,因为大量的公共数据都是通过其他公共数据计算得出的。例如,一个政府指数可能是根据调查数据构建的,而另

    阅读全文
    技术实践

    如何编写能够真正被编译成功的Linux内核模块

    Linux中的 内核模块 是一段较小的代码,可以在不重新构建整个内核的情况下被加载到正在运行的内核中。 这听起来很简单,但实际上,即使是最简单的模块也会产生大量相关的文件和数据:对象文件、元数据、导出的符号以及未解析的符号,最后还会生成一个与普通可执行文件截然不同的 .ko 文件。 下面是一个完整的、可以正常工作的Linux内核模块。它的代码仅有22行,其中7行是包含头文件和元数据: #include #include #include MODULE LICENSE("GPL"); MODULE AUTHOR("Chris Roy"); MODULE DESCRIPTION("一个最小的可加载

    阅读全文
    技术实践

    了解人工智能软件开发生命周期流程——构建智能代理功能的完整指南

    也许你可以理解这样的场景:本周,你用了同样的说明四次向别人解释人工智能模型的使用方法。 你反复讲解过团队是如何构建演示文稿的框架的,哪些检查步骤需要在部署之前完成,以及为什么测试数据库并不是文档中提到的那个。 每次你都要把这些内容重新写一遍,每次智能助手也能完成得不错,但每次新的会话开始时,一切都得从零开始。 而这正是 智能助手技能 所要解决的问题。 技能 实际上就是一个文件夹,其中只包含一个名为 Skill.md 的文件。智能助手在启动时会阅读其中的一行总结内容,而只有当真正需要时才会打开完整的说明文件。你只需把解释内容编写一次,将其与代码一起提交,那么团队中的每个智能助手就能访问这些信息,

    阅读全文