express和koa对比总结

439 阅读9分钟

初次学习express/koa,待着一些问题,寻找答案

一、express、koa、node三者直接什么关系

ExpressKoaNode.js 是三个常用的 JavaScript 服务端框架或平台,它们之间的关系如下:

  • Node.js:是一个基于 Chrome V8 引擎的 JavaScript 运行时,允许你使用 JavaScript 来编写服务器端应用。它为 JavaScript 提供了处理 HTTP 请求、文件系统操作等功能的底层 API,但它本身并不提供高级的 web 框架功能。
  • Express:是一个基于 Node.js 的 web 应用框架,提供了更简洁和更方便的方式来处理 HTTP 请求、路由、请求和响应等。它是 Node.js 上使用最多的 web 框架之一,封装了很多常用的功能,简化了开发过程。
  • Koa:是由 Express 的原始开发团队创建的一个新的 web 框架。与 Express 相比,Koa 更加轻量和灵活,使用了 async/await 来简化异步代码,同时通过中间件的方式处理请求。Koa 没有像 Express 那样内置的许多功能,而是更加模块化,开发者可以根据自己的需要选择和使用中间件。

1. Express、Koa、Node.js 三者的关系

  • Node.js 是基础,提供了底层的 HTTP 处理能力;
  • ExpressKoa 都是基于 Node.js 构建的 web 框架,分别提供了更高层的 web 开发接口和功能,简化了开发工作。
  • Express 更加适合快速开发和传统的 Web 应用;
  • Koa 更加现代化,注重性能和更灵活的中间件机制。

2. Express 服务的作用

Express 作为 Web 框架的作用主要是:

  • 提供了简单易用的 路由功能,让我们可以更方便地处理请求(如 GET、POST 等请求)。
  • 提供了 中间件机制,让开发者可以在请求和响应的生命周期中插入自定义逻辑。
  • 解析请求(如 ctx.request.queryreq.query),帮助我们获取 URL 中的查询参数、请求体、Cookies 等。
  • 简化响应(如 ctx.responseres),让开发者能更简便地返回 HTTP 响应。

3. Koa 的服务作用:

Koa 的优势在于使用 async/await 使得异步操作更加简洁和易于管理。

<1>. HTTP 请求和响应处理: Koa 本质上是一个 Web 框架,它通过底层的 HTTP API,处理客户端发来的请求,并根据业务逻辑生成响应返回给客户端。Koa 提供了对请求和响应的管理,允许你轻松处理 URL、请求参数、请求头、请求体等。

-   **请求对象**(`ctx.request`):用来访问客户端发来的请求数据。
-   **响应对象**(`ctx.response`):用来处理和返回响应数据。

示例:

```
const Koa = require('koa');
const app = new Koa();

app.use(async (ctx) => {
    ctx.body = 'Hello, Koa!';
});

app.listen(3000, () => {
    console.log('Server is running on http://localhost:3000');
});
```

这里的 `ctx.body` 就是响应内容,`ctx.request` 是请求内容。

<2>. 中间件机制: Koa 的中间件系统是它最强大的部分之一。Koa 使用 async/await 或者 生成器(generators) 来实现中间件的顺序执行,使得代码更为简洁和易于理解。每个中间件都可以在请求和响应之间执行某些操作,进行日志记录、身份验证、数据处理等。

-   Koa 中的每个中间件都有对请求的 **“前置处理”** 和 **“后置处理”** 的能力。
-   中间件的执行顺序非常明确:从上到下、按顺序执行。

示例:

```
app.use(async (ctx, next) => {
    console.log('Middleware 1');
    await next();  // 调用下一个中间件
});

app.use(async (ctx) => {
    console.log('Middleware 2');
    ctx.body = 'Hello, World!';
});
```

输出:

```
Middleware 1
Middleware 2
```

<3>. 请求路由处理: Koa 本身并不内置路由功能,但可以通过第三方中间件(如 koa-router)来实现路由处理功能。路由用来处理不同 URL 路径的请求,例如,处理 /home 路径和 /user 路径的不同请求。

示例:

```
const Router = require('koa-router');
const router = new Router();

router.get('/home', async (ctx) => {
    ctx.body = 'Home Page';
});

router.get('/user', async (ctx) => {
    ctx.body = 'User Page';
});

app.use(router.routes()).use(router.allowedMethods());
```

<4>. 错误处理: Koa 提供了完善的错误处理机制,通过中间件的方式处理应用程序中的错误。在 Koa 中,错误可以在中间件中捕获并进行处理,避免整个应用崩溃。

app.use(async (ctx, next) => {
    try {
        await next();
    } catch (err) {
        ctx.status = err.status || 500;
        ctx.body = { message: err.message };
    }
});

<5>. 响应体管理: Koa 提供了非常灵活的方式来处理响应体,支持返回 JSON、HTML 或其他格式的响应数据。你可以自定义 ctx.body,它可以是字符串、对象、流等。

app.use(async (ctx) => {
    ctx.body = { message: 'Hello, Koa!' };  // 返回 JSON 格式
});

<6>. 性能优化: Koa 的设计理念让它能够处理大量并发请求,并在高负载情况下保持良好的性能。由于 Koa 的中间件是基于 async/await 的,开发者能写出更加高效的异步代码,避免了回调地狱(callback hell)的问题。

好的!接下来我将从定义路由、监听端口、中间件这三个方面,分别讲解 Koa 和 Express 的用法,并进行对比。这将帮助你更好地理解它们的异同点。

二、从路由、中间件、监听端口了解koa和express

1. 定义路由

Express

Express 提供了非常直观的路由定义方式,支持多种 HTTP 方法(如 getpostputdelete 等)。

const express = require('express');
const app = express();

// GET 请求
app.get('/', (req, res) => {
    res.send('Hello, this is the home page!');
});

// POST 请求
app.post('/submit', (req, res) => {
    res.send('Form submitted!');
});

// 路由参数
app.get('/user/:id', (req, res) => {
    const userId = req.params.id;
    res.send(`User ID: ${userId}`);
});

// 查询字符串
app.get('/search', (req, res) => {
    const query = req.query.q;
    res.send(`You searched for: ${query}`);
});

特点

  • 路由方法直接对应 HTTP 方法(如 app.getapp.post)。
  • 使用 req.params 获取路由参数,使用 req.query 获取查询字符串参数。

Koa

Koa 的路由功能需要通过中间件(如 koa-router)来实现,它本身没有内置的路由功能。

const Koa = require('koa');
const Router = require('@koa/router');
const app = new Koa();
const router = new Router();

// GET 请求
router.get('/', async (ctx) => {
    ctx.body = 'Hello, this is the home page!';
});

// POST 请求
router.post('/submit', async (ctx) => {
    ctx.body = 'Form submitted!';
});

// 路由参数
router.get('/user/:id', async (ctx) => {
    const userId = ctx.params.id;
    ctx.body = `User ID: ${userId}`;
});

// 查询字符串
router.get('/search', async (ctx) => {
    const query = ctx.query.q;
    ctx.body = `You searched for: ${query}`;
});

// 使用路由中间件
app.use(router.routes()).use(router.allowedMethods());

特点

  • 需要使用 koa-router 中间件来实现路由功能。
  • 路由处理函数是 async 函数,使用 ctx 对象来访问请求和响应数据。
  • ctx.params 获取路由参数,ctx.query 获取查询字符串参数。

2. 监听端口

Express

在 Express 中,监听端口是通过调用 app.listen() 方法完成的。

const PORT = 3000;
app.listen(PORT, () => {
    console.log(`Server is running on http://localhost:${PORT}`);
});

Koa

在 Koa 中,监听端口是通过调用 app.listen() 方法完成的,与 Express 类似。

const PORT = 3000;
app.listen(PORT, () => {
    console.log(`Server is running on http://localhost:${PORT}`);
});

对比

  • 两者在监听端口的方式上几乎完全相同,都使用 app.listen() 方法。

3. 中间件

Express

Express 原生支持中间件,中间件可以访问请求对象(req)、响应对象(res)和下一个中间件函数(next)。

// 全局中间件
app.use((req, res, next) => {
    console.log('Time:', Date.now());
    next();
});

// 内置中间件
app.use(express.json()); // 解析 JSON 请求体
app.use(express.urlencoded({ extended: true })); // 解析 URL 编码的请求体

// 第三方中间件(如日志中间件)
const morgan = require('morgan');
app.use(morgan('dev'));

// 路由中间件
app.use('/api', require('./routes/api'));

特点

  • 中间件通过 app.use()app.METHOD() 添加。
  • 中间件函数的签名是 (req, res, next)
  • 可以使用内置中间件、第三方中间件或自定义中间件。

Koa

Koa 的中间件机制基于洋葱模型(onion model),中间件的执行顺序是先执行当前中间件的同步代码,然后执行下一个中间件,最后执行当前中间件的异步代码。

// 全局中间件
app.use(async (ctx, next) => {
    console.log('Before request:', Date.now());
    await next();
    console.log('After request:', Date.now());
});

// 第三方中间件(如日志中间件)
const logger = require('koa-logger');
app.use(logger());

// 路由中间件
app.use(router.routes()).use(router.allowedMethods());

特点

  • 中间件通过 app.use() 添加。
  • 中间件函数的签名是 async (ctx, next)
  • 使用 await next() 来调用下一个中间件,支持异步操作。

总结对比

特性ExpressKoa
路由定义内置路由功能,使用 app.getapp.post 等方法。需要通过 koa-router 中间件实现路由功能。
监听端口使用 app.listen()使用 app.listen()
中间件基于回调函数的中间件机制,使用 (req, res, next)基于洋葱模型的异步中间件机制,使用 async (ctx, next)
请求对象使用 reqres 对象。使用 ctx 对象,统一访问请求和响应数据。
学习曲线更适合初学者,语法直观,功能丰富。更灵活,但需要理解洋葱模型和异步中间件机制。
性能性能良好,但中间件机制相对复杂时可能稍逊于 Koa。由于其简洁的中间件机制,性能通常更优。

三、express---req 和 koa---ctx

在 Express 和 Koa 中,reqctx 是两个非常重要的概念,它们分别代表了请求对象和上下文对象。接下来我将详细解释它们的含义以及它们是如何被创建和使用的。


在 Express 中的 req

1. req 是什么?

在 Express 中,req 是一个请求对象(Request 对象),它封装了客户端发送到服务器的 HTTP 请求的所有信息。req 对象包含了请求头、请求体、查询字符串、路由参数等信息。它是一个内置的 Node.js HTTP 模块中的 http.IncomingMessage 的扩展。

2. req 是如何来的?

当客户端(如浏览器或 API 客户端)向 Express 服务器发送一个 HTTP 请求时,Node.js 的底层 HTTP 模块会解析这个请求,并创建一个 http.IncomingMessage 对象。Express 在这个基础上扩展了 req 对象,并将其传递给路由处理函数。

在 Express 的生命周期中,req 对象的创建和传递过程如下:

  1. 客户端发送请求:客户端通过 HTTP 协议发送请求到 Express 服务器。
  2. Node.js 解析请求:Node.js 的 HTTP 模块接收到请求后,解析请求头、请求体等信息,并创建一个 http.IncomingMessage 对象。
  3. Express 扩展 req:Express 在 http.IncomingMessage 的基础上添加了一些额外的属性和方法,例如 req.paramsreq.queryreq.body 等。
  4. 传递给路由处理函数:Express 将扩展后的 req 对象传递给对应的路由处理函数。
3. req 的常见属性和方法
  • req.method:请求方法(如 GETPOST)。
  • req.url:请求的 URL。
  • req.headers:请求头信息。
  • req.query:查询字符串参数(通过 URL 的查询部分,如 ?key=value)。
  • req.params:路由参数(通过 URL 的路径部分,如 /user/:id)。
  • req.body:请求体内容(通常需要通过中间件解析,如 express.json()express.urlencoded())。
  • req.ip:客户端的 IP 地址。
  • req.path:请求的路径部分。

示例:

app.get('/user/:id', (req, res) => {
    console.log(req.method); // 输出请求方法,如 "GET"
    console.log(req.url); // 输出请求的完整 URL,如 "/user/123?name=John"
    console.log(req.headers); // 输出请求头信息
    console.log(req.query); // 输出查询字符串参数,如 { name: 'John' }
    console.log(req.params); // 输出路由参数,如 { id: '123' }
    console.log(req.body); // 输出请求体内容(需要中间件支持)
    res.send(`User ID: ${req.params.id}`);
});

在 Koa 中的 ctx

1. ctx 是什么?

在 Koa 中,ctx 是上下文对象(Context 对象),它封装了当前请求和响应的所有信息。ctx 是 Koa 的核心概念之一,它将请求对象(req)和响应对象(res)封装在一起,提供了一个统一的接口来访问和操作请求和响应数据。通过 ctx,开发者可以更方便地处理请求和响应,而无需像在 Express 中那样分别操作 reqres

2. ctx 是如何来的?

ctx 对象是由 Koa 框架在处理每个请求时自动创建的。它的创建过程如下:

  1. 客户端发送请求:客户端通过 HTTP 协议发送请求到 Koa 服务器。
  2. Koa 创建 ctx:Koa 接收到请求后,会自动创建一个 ctx 对象。这个对象内部包含了 Node.js 的 reqres 对象,并对其进行了封装和扩展。
  3. 传递给中间件:Koa 将 ctx 对象传递给每个中间件函数,中间件可以通过 ctx 来访问请求和响应数据。

ctx 的设计目标是提供一个简洁、统一的接口,让开发者能够更方便地操作请求和响应,而无需直接处理底层的 reqres 对象。

3. ctx 的常见属性和方法
  • ctx.request:请求对象,包含请求相关的属性和方法。

    • ctx.request.method:请求方法。
    • ctx.request.url:请求的 URL。
    • ctx.request.headers:请求头信息。
    • ctx.request.query:查询字符串参数。
    • ctx.request.body:请求体内容(需要中间件支持)。
  • ctx.response:响应对象,包含响应相关的属性和方法。

    • ctx.response.status:响应状态码。
    • ctx.response.body:响应体内容。
    • ctx.response.set:设置响应头。
  • ctx.params:路由参数。

  • ctx.ip:客户端的 IP 地址。

  • ctx.path:请求的路径部分。

  • ctx.query:查询字符串参数。

  • ctx.body:直接设置响应体内容(等同于 ctx.response.body)。

示例:

const Koa = require('koa');
const Router = require('@koa/router');
const app = new Koa();
const router = new Router();

router.get('/user/:id', async (ctx) => {
    console.log(ctx.request.method); // 输出请求方法,如 "GET"
    console.log(ctx.request.url); // 输出请求的完整 URL,如 "/user/123?name=John"
    console.log(ctx.request.headers); // 输出请求头信息
    console.log(ctx.request.query); // 输出查询字符串参数,如 { name: 'John' }
    console.log(ctx.params); // 输出路由参数,如 { id: '123' }
    console.log(ctx.request.body); // 输出请求体内容(需要中间件支持)

    ctx.body = `User ID: ${ctx.params.id}`; // 设置响应体内容
});

app.use(router.routes()).use(router.allowedMethods());

app.listen(3000, () => {
    console.log('Server is running on http://localhost:3000');
});

总结

特性Express 的 reqKoa 的 ctx
含义请求对象,封装了客户端发送的 HTTP 请求的所有信息。上下文对象,封装了请求和响应的所有信息,提供统一接口。
来源由 Node.js 的 HTTP 模块解析请求后创建,Express 进行扩展。由 Koa 自动创建,封装了 Node.js 的 reqres 对象。
使用方式需要分别操作 reqres 对象。通过 ctx 统一访问请求和响应数据,如 ctx.requestctx.response
常见属性req.methodreq.urlreq.headersreq.queryreq.paramsreq.body 等。ctx.request.methodctx.request.urlctx.request.headersctx.queryctx.paramsctx.request.body 等。
响应设置使用 res.send()res.json() 等方法。使用 ctx.body 设置响应体,ctx.status 设置状态码等。

通过对比可以看到,Koa 的 ctx 提供了一个更简洁和统一的接口来处理请求和响应,而 Express 的 reqres 则更直观地反映了 HTTP 请求和响应的结构。