阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 与Express/Koa框架

与Express/Koa框架

作者:陈川 阅读数:33350人阅读 分类: TypeScript

Express/Koa框架概述

Express和Koa都是Node.js生态中流行的Web应用框架。Express以其简单、灵活著称,而Koa由Express团队开发,采用更现代的异步处理机制。两者都支持TypeScript开发,为构建服务器端应用提供了强大支持。

基本架构对比

Express采用传统的回调函数处理请求,中间件通过next()函数控制流程:

import express from 'express';

const app = express();

app.use((req, res, next) => {
  console.log('Middleware 1');
  next();
});

app.get('/', (req, res) => {
  res.send('Hello Express');
});

Koa则基于ES6的async/await语法,使用上下文对象封装请求和响应:

import Koa from 'koa';

const app = new Koa();

app.use(async (ctx, next) => {
  console.log('Middleware 1');
  await next();
});

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

中间件机制差异

Express中间件是线性执行的,每个中间件必须显式调用next()才能继续:

// Express中间件示例
app.use((req, res, next) => {
  // 必须调用next()否则请求会挂起
  if (req.path === '/admin') {
    return res.status(403).end();
  }
  next();
});

Koa中间件形成洋葱模型,支持更灵活的流程控制:

// Koa中间件洋葱模型
app.use(async (ctx, next) => {
  console.log('进入中间件1');
  await next(); // 执行下游中间件
  console.log('离开中间件1'); // 会等所有下游中间件执行完
});

app.use(async ctx => {
  ctx.body = '响应内容';
});

路由系统实现

Express内置了基础路由功能:

const router = express.Router();

router.get('/users', (req, res) => {
  res.json([{ id: 1, name: 'Alice' }]);
});

app.use('/api', router);

Koa需要配合koa-router等第三方库:

import Router from '@koa/router';

const router = new Router();

router.get('/users', ctx => {
  ctx.body = [{ id: 1, name: 'Alice' }];
});

app.use(router.routes());

错误处理方式

Express通常使用错误优先回调:

app.get('/error', (req, res, next) => {
  fs.readFile('/不存在的文件', (err, data) => {
    if (err) return next(err);
    res.send(data);
  });
});

// 错误处理中间件
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).send('出错了!');
});

Koa利用async/await的try/catch机制:

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

router.get('/error', async ctx => {
  throw new Error('测试错误');
});

请求上下文扩展

Express需要手动扩展请求对象:

declare global {
  namespace Express {
    interface Request {
      user?: User;
    }
  }
}

app.use((req, res, next) => {
  req.user = { id: 1, name: 'Alice' };
  next();
});

Koa的上下文对象更易于扩展:

import { Context } from 'koa';

declare module 'koa' {
  interface Context {
    user: User;
  }
}

app.use(async (ctx, next) => {
  ctx.user = { id: 1, name: 'Alice' };
  await next();
});

性能考量

Koa的轻量级设计通常表现更好:

// Koa基准测试示例
const Koa = require('koa');
const app = new Koa();

app.use(ctx => {
  ctx.body = 'Hello World';
});

// Express基准测试示例
const express = require('express');
const app = express();

app.get('/', (req, res) => {
  res.send('Hello World');
});

测试数据显示Koa在简单请求处理上比Express快约10-15%,主要得益于更精简的核心设计。

生态系统比较

Express拥有更丰富的中间件生态:

// Express常用中间件
app.use(require('body-parser').json());
app.use(require('helmet')());
app.use(require('cors')());

Koa中间件通常更现代化:

// Koa常用中间件
app.use(require('koa-bodyparser')());
app.use(require('koa-helmet')());
app.use(require('@koa/cors')());

TypeScript集成实践

Express项目典型配置:

// tsconfig.json
{
  "compilerOptions": {
    "target": "ES2018",
    "module": "commonjs",
    "esModuleInterop": true,
    "strict": true,
    "skipLibCheck": true
  }
}

Koa项目推荐配置:

// tsconfig.json
{
  "compilerOptions": {
    "target": "ES2018",
    "module": "commonjs",
    "esModuleInterop": true,
    "strict": true,
    "noImplicitAny": false
  }
}

实际项目结构示例

Express典型项目结构:

src/
├── controllers/
│   ├── user.controller.ts
├── middlewares/
│   ├── auth.middleware.ts
├── routes/
│   ├── user.route.ts
├── app.ts

Koa推荐项目结构:

src/
├── controllers/
│   ├── user.controller.ts
├── middlewares/
│   ├── error.middleware.ts
├── routers/
│   ├── user.router.ts
├── app.ts

数据库集成示例

Express集成Mongoose:

import mongoose from 'mongoose';

mongoose.connect('mongodb://localhost/test');

const UserSchema = new mongoose.Schema({
  name: String
});

const User = mongoose.model('User', UserSchema);

app.get('/users', async (req, res) => {
  const users = await User.find();
  res.json(users);
});

Koa集成TypeORM:

import { createConnection } from 'typeorm';

createConnection().then(connection => {
  const userRepository = connection.getRepository(User);
  
  router.get('/users', async ctx => {
    ctx.body = await userRepository.find();
  });
});

部署注意事项

Express应用部署配置示例:

// PM2配置文件
module.exports = {
  apps: [{
    name: 'express-app',
    script: './dist/app.js',
    instances: 'max',
    exec_mode: 'cluster'
  }]
}

Koa应用部署优化:

// PM2配置文件
module.exports = {
  apps: [{
    name: 'koa-app',
    script: './dist/app.js',
    instances: 0, // 根据CPU核心数自动扩展
    exec_mode: 'cluster',
    max_memory_restart: '1G'
  }]
}

现代前端集成

Express配合Webpack:

const webpack = require('webpack');
const middleware = require('webpack-dev-middleware');

const compiler = webpack(webpackConfig);
app.use(middleware(compiler));

Koa配合Vite:

import { createServer as createViteServer } from 'vite';

const vite = await createViteServer({
  server: { middlewareMode: true }
});

app.use(vite.middlewares);

测试策略实现

Express单元测试示例:

import request from 'supertest';

describe('GET /users', () => {
  it('should return user list', async () => {
    const res = await request(app)
      .get('/users')
      .expect(200);
    
    expect(res.body).toBeInstanceOf(Array);
  });
});

Koa集成测试示例:

import test from 'ava';
import { createApp } from '../src/app';

test('GET /users', async t => {
  const app = createApp();
  const res = await app.inject({
    method: 'GET',
    url: '/users'
  });
  
  t.is(res.statusCode, 200);
  t.true(Array.isArray(JSON.parse(res.payload)));
});

安全实践对比

Express安全中间件配置:

app.use(helmet());
app.use(cors({
  origin: ['https://example.com']
}));
app.use(rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 100
}));

Koa安全配置示例:

app.use(require('koa-helmet')());
app.use(require('@koa/cors')({
  origin: ctx => {
    const allowed = ['https://example.com'];
    return allowed.includes(ctx.request.header.origin) ? 
      ctx.request.header.origin : '';
  }
}));

微服务架构适配

Express作为微服务网关:

import { createProxyMiddleware } from 'http-proxy-middleware';

app.use('/api/users', createProxyMiddleware({
  target: 'http://user-service:3000',
  changeOrigin: true
}));

Koa实现API聚合:

router.get('/aggregated', async ctx => {
  const [userRes, orderRes] = await Promise.all([
    axios.get('http://user-service/users'),
    axios.get('http://order-service/orders')
  ]);
  
  ctx.body = {
    users: userRes.data,
    orders: orderRes.data
  };
});

实时功能实现

Express配合Socket.io:

import { createServer } from 'http';
import { Server } from 'socket.io';

const httpServer = createServer(app);
const io = new Server(httpServer);

io.on('connection', socket => {
  socket.on('chat message', msg => {
    io.emit('chat message', msg);
  });
});

Koa使用WebSockets:

import WebSocket from 'ws';

const wss = new WebSocket.Server({ server });

wss.on('connection', ws => {
  ws.on('message', message => {
    wss.clients.forEach(client => {
      if (client !== ws && client.readyState === WebSocket.OPEN) {
        client.send(message);
      }
    });
  });
});

日志系统集成

Express日志配置:

import morgan from 'morgan';

app.use(morgan('combined'));
app.use((req, res, next) => {
  console.log(`[${new Date().toISOString()}] ${req.method} ${req.path}`);
  next();
});

Koa结构化日志:

import koaLogger from 'koa-logger';
import { createLogger, format, transports } from 'winston';

const logger = createLogger({
  transports: [new transports.Console()]
});

app.use(koaLogger(str => {
  logger.info(str);
}));

配置管理方案

Express多环境配置:

import dotenv from 'dotenv';

dotenv.config({
  path: `.env.${process.env.NODE_ENV || 'development'}`
});

app.set('port', process.env.PORT || 3000);

Koa配置中心集成:

import config from 'config';

interface AppConfig {
  port: number;
  db: {
    host: string;
    port: number;
  };
}

const appConfig = config.get<AppConfig>('app');

app.listen(appConfig.port);

容器化部署

Express Dockerfile示例:

FROM node:16-alpine

WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .

EXPOSE 3000
CMD ["node", "dist/app.js"]

Koa容器优化:

FROM node:16-alpine

WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .

RUN npm run build
EXPOSE 3000
CMD ["node", "--enable-source-maps", "dist/app.js"]

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

上一篇:与Node.js后端开发

下一篇:与GraphQL配合

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌