第 6 卷
- 核心属性: stack(中间件/路由规则数组,每个元素是 Layer 实例,包含路径、中间件函
数、匹配规则);
- use() 方法:向 stack 中添加中间件Layer,支持路径前缀匹配;
- HTTP方法(如 get()):创建路由Layer,关联请求方法、路径和处理函数,存入 stack。
- 请求处理流程
当HTTP请求到达时,服务器调用 app(req, res)( Application 类实现了 req, res, next 的请求处理函数),核心逻辑:
- 初始化 req(扩展原生req,如添加 req.query、 req.params)和 res(扩展原生
res,如添加 res.send()、 res.json());
- 调用 _router.handle(req, res, done),启动路由匹配和中间件执行;
- Router.handle() 遍历 stack 中的Layer,通过 path-to-regexp 库匹配请求路径和方
法;
- 匹配成功后,执行Layer中的中间件函数,通过 next() 控制流程( next() 内部是递归遍历
stack的逻辑);
- 若所有中间件执行完成仍未返回响应,调用 done(),触发404错误。
- 响应方法实现
Express扩展了原生 res 对象,如 res.send()(源码在 lib/response.js):
- 自动判断响应数据类型(字符串、JSON、Buffer),设置对应的 Content-Type;
- 处理响应状态码,调用原生 res.end() 发送响应。
- Koa源码实现核心
Koa(源码核心文件: lib/application.js)基于ES6+,设计更简洁,核心是“洋葱模型中间件+上下文封装”,工作流程如下:
- 核心类:Application
koa() 返回 Application 实例,继承自 events.EventEmitter。
- 核心属性: middleware(中间件数组,存储所有通过 use() 注册的中间件函数)、
context(上下文对象原型,封装 req、 res);
- 核心方法:
- use(fn):向 middleware 数组添加中间件(需是 (ctx, next) => { ... } 格式,
若为Generator函数,需通过 koa-convert 转换);
- listen():创建HTTP服务器,调用 http.createServer(this.callback()),其
中 this.callback() 返回请求处理函数。
app.context;
- ctx.req / ctx.res:原生req/res对象;
- ctx.request / ctx.response:Koa扩展的请求/响应对象(如 ctx.request.url、
ctx.response.body);
- 源码中通过getter/setter简化操作,如 ctx.body 对应 ctx.response.body,
ctx.status 对应 ctx.response.status。
- 洋葱模型中间件实现
Koa的核心是 compose 函数(源码在 lib/compose.js),该函数接收中间件数组,返回一个可执行的中间件函数,实现洋葱模型的核心逻辑:
- compose 函数内部通过递归调用 next(),将中间件串联起来;
- 每个中间件的 next() 函数对应下一个中间件的执行结果(Promise);
- 当中间件中调用 await next() 时,会暂停当前中间件,执行下一个中间件,直到最内层中间
件执行完成后,再反向执行每个中间件的后续逻辑。
compose 核心源码简化:
function compose(middleware) { return function (ctx, next) {
let index = -1;
function dispatch(i) { if (i <= index) return Promise.reject(new Error("next()
called multiple times"));
index = i;
let fn = middleware[i];
if (i === middleware.length) fn = next;
if (!fn) return Promise.resolve();
try { return Promise.resolve(fn(ctx, dispatch.bind(null, i + 1)));
} catch (err) { return Promise.reject(err);
} }
return dispatch(0);
};
}
- 返回请求处理函数 (req, res) => { ... },该函数创建 ctx 实例( ctx =
app.createContext(req, res));
- 执行 fn(ctx),启动中间件链条;
- 中间件执行完成后,根据 ctx.body、 ctx.status 等信息,调用 respond() 函数发送响
应(源码在 lib/application.js);
- 若执行过程中出现错误,触发 error 事件,若未监听 error 事件,直接抛出错误。
九、什么是数据库? 为什么需要数据库存储?
- 数据库的核心定义
数据库(Database,简称DB)是**按照数据结构组织、存储和管理数据的仓库**,本质是一个或多个结构化的数据集合。它由数据库管理系统(Database Management System,简称DBMS)进行管
理,DBMS提供数据定义、数据操作(增删改查)、数据安全、数据备份等核心功能。
简单来说,数据库就是“专业的数据管家”,负责高效、安全地存储和管理大量数据,供应用程序(如网站、APP)调用。
- 需要数据库存储的核心原因
在没有数据库的时代,数据通常存储在文件中(如txt、excel),但随着数据量增长和应用复杂度提升,文件存储的弊端凸显,数据库的价值由此体现,核心原因如下:
- 解决数据冗余问题
文件存储中,相同数据可能需要在多个文件中重复存储(如用户信息在订单文件、评论文件中都需保存),导致数据冗余(重复存储),浪费存储空间且难以维护(修改一处数据需同步修改多
处)。
数据库通过数据规范化设计(如关系型数据库的表结构设计),将数据拆分到不同表中,通过关联关系避免重复存储,降低冗余。
- 保证数据一致性与完整性
文件存储中,数据修改容易出现“部分修改成功”的情况(如修改用户姓名,仅修改了订单文件中的姓名,评论文件中的姓名未修改),导致数据不一致。
数据库通过约束(如主键约束、外键约束、唯一性约束)和事务(原子性、一致性、隔离性、持久性,ACID)保证数据完整性和一致性:
- 约束:防止无效数据插入(如主键不能为空、唯一);
- 事务:确保一组数据操作要么全部成功,要么全部失败(如转账操作,扣款和到账必须同时完
成)。
全站分卷索引 · 12 卷
- 1第 1 卷
- 2第 2 卷
- 四、Node.js中的非阻塞I/O(Non-blocking I/O)模型
- • 阻塞I/O:主线程发起I/O操作后,暂停执行所有代码,直到I/O操作完成(如传统的PHP文件读
- • 非阻塞I/O:主线程发起I/O操作后,立即返回一个状态(未完成),并继续执行后续代码;I/O操作
- • exports:是 module.exports 的一个引用(快捷方式),默认是一个空对象 {},只能通
- • module.exports:是模块的真正导出对象,Node.js最终导出的是 module.exports 的
- ◦ 若为相对路径( ./、 ../)或绝对路径,直接按路径查找对应
- ◦ 若为第三方模块(如 express),会在当前目录的 node_modules 文件夹中查找,若找不
- 3第 3 卷
- 4第 4 卷
- 5第 5 卷
- 6第 6 卷
- 7第 7 卷
- 8第 8 卷
- 9第 9 卷
- 10第 10 卷
- 11第 11 卷
- 12第 12 卷