前端栈学习-服务器(九)、Node.js和NPM
什么是服务器?
服务器是一个响应客户端请求并提供服务的系统。在 Web 开发中,服务器通过 请求-响应 的模式与客户端通信。
为什么需要服务器?
- 文件访问:集中存储和管理文件。
- 集中化:统一管理数据和业务逻辑。
- 安全性:通过服务器控制访问权限,保护数据。
服务器如何工作?
每个服务器都绑定到计算机上的一个端口(如 80、443)。客户端通过 协议://域名:端口 的方式连接到服务器。
例如:
1 | http://localhost:3000 |
localhost
表示本地计算机。3000
是服务器监听的端口。
使用本机作为服务器
实际上,每台计算机都可以运行服务器代码,只需要启动一个服务程序。例如:
1 | const express = require("express"); |
运行此代码后,访问 http://localhost:3000
,你将看到浏览器中显示 “Hello, Server!”。
Node.js 和 Express.js
什么是 Node.js?
Node.js 是一个运行 JavaScript 的服务器端平台,基于 Chrome 的 V8 引擎。它允许你在服务器上运行 JavaScript。
什么是 Express.js?
Express.js 是 Node.js 上的一个轻量级 Web 框架,用于快速构建服务器端应用程序。它简化了路由、中间件和请求处理。
以下是一个使用 Express.js 构建简单 API 的示例:
1 | const express = require("express"); |
访问 /api
路径会返回 JSON 数据:
1 | { |
我们需要从特殊到不特殊地定义端点
文件结构
/client - 包含我们所有的 React 代码、组件、页面、工具等内容。(前端)
/server - 包含我们所有的后端代码。
其他文件 - 由团队人员设置,用于配置 React 应用。如有疑问,请随时咨询相关人员。
NPM(Node 包管理器)
NPM 是 Node.js 的包管理工具,用于安装和管理依赖。
常用命令
npm install
:安装依赖。npm start
:运行项目。npm run <script>
:运行在package.json
中定义的脚本。npm run hotloader
:运行开发环境(具体任务由脚本配置)。
项目结构:
- package.json:存储项目元信息,例如依赖项、脚本等。
- node_modules:包含安装的依赖。
例如,以下是典型的 package.json
文件:
1 | { |
通过运行 npm install
,NPM 会下载 express
并将其存储在 node_modules
文件夹中。
创建 API 端点
中间件(Middleware)
中间件是 Express.js 的核心概念,用于在请求和响应之间插入额外的处理逻辑。
中间件是当请求到达时运行的函数
可以在路由处理器之前运行
路由处理器也是中间件!
中间件的作用
- 修改请求:为请求对象添加属性。
- 预处理:解析请求体、验证用户身份等。
- 处理错误:捕获并处理异常。
- 提供静态内容
中间件的定义和调用
Express.js 使用 app.use(middlewareFunction)
注册中间件。
app.use()
是 Express 提供的方法,用于注册中间件或路由模块。
- 中间件:对请求进行预处理(如解析 JSON 数据、身份验证等)。
- 路由模块:定义多个路由(如 GET、POST)来处理不同的路径。
错误中间件需要四个参数:error
、req
、res
、next
。
- 在接收到请求和执行端点代码之间运行的代码
- 中间件就像流水线上的工人
- 它们可以传递请求并修改/返回响应
- 中间件按定义的顺序被调用
添加中间件
通过调用app.use()
来注册中间件
例子:
基本结构和调用
1 | const express = require("express"); |
next()
的作用
next()
是什么?
next()
是 Express 中间件函数提供的一个回调,用于将请求传递给下一个中间件或路由处理器。- 它确保中间件链能够按顺序执行。
如果没有调用
next()
会怎么样?
如果中间件没有调用
next()
,请求会被挂起,客户端将无法接收到响应。示例:
1
2
3
4
5
6
7
8
9
10
11
12 app.use((req, res, next) => {
console.log("Middleware 1");
// 没有调用 next(),请求停在这里
});
app.get("/", (req, res) => {
res.send("Hello, world!");
});
app.listen(3000, () => {
console.log("Server is running on http://localhost:3000");
});
- 当客户端访问
/
时,Middleware 1
会打印日志,但请求不会继续执行到路由/
的处理器,客户端最终会超时。正确使用
next()
的示例
1
2
3
4
5
6
7
8
9
10
11
12 app.use((req, res, next) => {
console.log("Middleware 1");
next(); // 将请求交给下一个中间件或路由
});
app.get("/", (req, res) => {
res.send("Hello, world!");
});
app.listen(3000, () => {
console.log("Server is running on http://localhost:3000");
});
- 输出顺序:
- 服务端日志:
Middleware 1
- 客户端响应:
Hello, world!
next()
的核心作用
传递控制权
- 将当前请求传递给下一个符合条件的中间件或路由处理器。
- 如果没有调用
next()
,请求会被中止。错误处理
- 当调用
next(err)
时,可以将错误传递给错误处理中间件。
1
2
3
4
5
6
7
8
9 app.use((req, res, next) => {
const error = new Error("Something went wrong");
next(error); // 将错误传递到错误处理中间件
});
app.use((err, req, res, next) => {
console.error(err.message);
res.status(500).send("Server Error");
});控制执行顺序
- 中间件是按照定义的顺序执行,
next()
决定是否让请求继续向下一个中间件流转。
为特定路径定义中间件
1 | app.use("/api", (req, res, next) => { |
解析:
/api
中间件只会对/api
开头的路径生效,如/api/test
。- 中间件在处理路径匹配后可以继续调用
next()
将控制权交给下一个中间件或路由。
用中间件处理 JSON 数据
1 | app.use(express.json()); // 解析 JSON 请求体 |
解析:
express.json()
是 Express 内置的 JSON 解析中间件。- 用于处理请求体中的 JSON 数据,将其解析为 JavaScript 对象并赋值给
req.body
。
Catch All 路由和 React 集成
在使用 React 和 Express 构建全栈应用时,通常需要处理 “Catch All” 路由以支持前端的路由功能。
示例代码
1 | const express = require("express"); |
解释
require("express")
是什么?require
是 Node.js 中用于引入模块的函数。express
是一个流行的 Node.js 框架,用于构建 Web 应用程序和 API。require("express")
导入了 Express 框架的主模块。- 为什么需要它?
- Express 提供了简单的 API 来创建服务器、定义路由、中间件和处理请求/响应。
const path = require("path");
path
是 Node.js 内置的模块,用于处理文件和目录路径。通过使用
path
模块,你可以构建跨平台兼容的路径,避免手动拼接路径时出错。作用:这行代码使你能够使用
path
模块来处理路径相关的操作,确保路径的兼容性。
const app = express();
这行代码调用了
express()
函数,创建了一个 Express 应用实例,并赋值给app
变量。app
是你的 Express 应用,所有路由和中间件都会在这个实例上定义和处理。作用:这行代码初始化了一个 Express 应用,允许你在
app
上定义路由、添加中间件等。
express.static(reactPath)
提供 React 的静态资源(如 JS、CSS 文件)。什么是静态文件?
静态文件是指无需服务器动态处理、可以直接传送到客户端的资源文件,例如:
- HTML 文件
- CSS 样式表
- JavaScript 脚本
- 图片(如 PNG、JPG)
- 其他如字体文件、音频文件等
它们是直接存储在文件系统中的固定内容,不依赖用户输入进行动态生成。
静态文件的作用
提供网站的前端资源(比如布局、样式、交互等)。
高效地向客户端传递文件内容,减少服务器的计算负担。
app.get("\*", (req, res) => { ... });
这行代码定义了一个 通配符路由(catch-all route),
*
表示匹配任何路径。当用户访问任何未被前面定义的路由时,都会被这个路由捕获。
这里的
res.sendFile(path.join(reactPath, "index.html"));
表示服务器返回reactPath
目录下的index.html
文件。作用:捕获所有未匹配的路径,并返回 React 应用的
index.html
文件。这是单页应用(SPA)的常见做法,当用户刷新或访问其他 URL 时,都会返回同一个index.html
文件,由 React 路由来处理页面切换。React 会通过前端路由(如 React Router)处理路径并渲染对应页面。
代码总体解释
这段代码是典型的 前后端分离 项目中的服务器端设置:
- 后端 API:通过
/api/test
提供一个简单的 API,当用户访问时,返回一个包含消息的 JSON 响应。 - 托管静态文件:通过
express.static()
中间件,将构建后的 React 应用文件夹(dist
)作为静态文件提供给前端。 - 单页应用支持:使用
*
路由捕获所有未定义的路径,返回 React 应用的index.html
,确保客户端的前端路由可以正常工作。
总结:这段代码主要实现了一个简易的后端服务,它提供了一个简单的 API 和 React 应用的静态文件托管,同时确保 React 应用能处理用户直接访问不同 URL 的情况。
运行服务器
- 同时运行两个服务器:
localhost:5050
:处理 React 热加载。localhost:3000
:Node.js 后端服务器。
- 测试后端用
localhost:3000
,查看网站用localhost:5050
。