← 返回目录

第 6 卷

第 6 / 12 卷日期:

  • 核心属性: stack(中间件/路由规则数组,每个元素是 Layer 实例,包含路径、中间件函

数、匹配规则);

  • use() 方法:向 stack 中添加中间件Layer,支持路径前缀匹配;
  • HTTP方法(如 get()):创建路由Layer,关联请求方法、路径和处理函数,存入 stack。
  1. 请求处理流程

当HTTP请求到达时,服务器调用 app(req, res)( Application 类实现了 req, res, next 的请求处理函数),核心逻辑:

  1. 初始化 req(扩展原生req,如添加 req.query、 req.params)和 res(扩展原生

res,如添加 res.send()、 res.json());

  1. 调用 _router.handle(req, res, done),启动路由匹配和中间件执行;
  2. Router.handle() 遍历 stack 中的Layer,通过 path-to-regexp 库匹配请求路径和方

法;

  1. 匹配成功后,执行Layer中的中间件函数,通过 next() 控制流程( next() 内部是递归遍历

stack的逻辑);

  1. 若所有中间件执行完成仍未返回响应,调用 done(),触发404错误。
  2. 响应方法实现

Express扩展了原生 res 对象,如 res.send()(源码在 lib/response.js):

  • 自动判断响应数据类型(字符串、JSON、Buffer),设置对应的 Content-Type;
  • 处理响应状态码,调用原生 res.end() 发送响应。
  1. Koa源码实现核心

Koa(源码核心文件: lib/application.js)基于ES6+,设计更简洁,核心是“洋葱模型中间件+上下文封装”,工作流程如下:

  1. 核心类: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。

  1. 洋葱模型中间件实现

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);

};

}

  1. 返回请求处理函数 (req, res) => { ... },该函数创建 ctx 实例( ctx =

app.createContext(req, res));

  1. 执行 fn(ctx),启动中间件链条;
  2. 中间件执行完成后,根据 ctx.body、 ctx.status 等信息,调用 respond() 函数发送响

应(源码在 lib/application.js);

  1. 若执行过程中出现错误,触发 error 事件,若未监听 error 事件,直接抛出错误。

九、什么是数据库? 为什么需要数据库存储?

  1. 数据库的核心定义

数据库(Database,简称DB)是**按照数据结构组织、存储和管理数据的仓库**,本质是一个或多个结构化的数据集合。它由数据库管理系统(Database Management System,简称DBMS)进行管

理,DBMS提供数据定义、数据操作(增删改查)、数据安全、数据备份等核心功能。

简单来说,数据库就是“专业的数据管家”,负责高效、安全地存储和管理大量数据,供应用程序(如网站、APP)调用。

  1. 需要数据库存储的核心原因

在没有数据库的时代,数据通常存储在文件中(如txt、excel),但随着数据量增长和应用复杂度提升,文件存储的弊端凸显,数据库的价值由此体现,核心原因如下:

  1. 解决数据冗余问题

文件存储中,相同数据可能需要在多个文件中重复存储(如用户信息在订单文件、评论文件中都需保存),导致数据冗余(重复存储),浪费存储空间且难以维护(修改一处数据需同步修改多

处)。

数据库通过数据规范化设计(如关系型数据库的表结构设计),将数据拆分到不同表中,通过关联关系避免重复存储,降低冗余。

  1. 保证数据一致性与完整性

文件存储中,数据修改容易出现“部分修改成功”的情况(如修改用户姓名,仅修改了订单文件中的姓名,评论文件中的姓名未修改),导致数据不一致。

数据库通过约束(如主键约束、外键约束、唯一性约束)和事务(原子性、一致性、隔离性、持久性,ACID)保证数据完整性和一致性:

  • 约束:防止无效数据插入(如主键不能为空、唯一);
  • 事务:确保一组数据操作要么全部成功,要么全部失败(如转账操作,扣款和到账必须同时完

成)。

全站分卷索引 · 12 卷
  1. 1第 1 卷
  2. 2第 2 卷
  3. 3第 3 卷
  4. 4第 4 卷
  5. 5第 5 卷
  6. 6第 6 卷
  7. 7第 7 卷
  8. 8第 8 卷
  9. 9第 9 卷
  10. 10第 10 卷
  11. 11第 11 卷
  12. 12第 12 卷