Q17: Koa.js 和 Express.js 有什么区别?Koa.js 的优势是什么?

60 阅读3分钟

Node.js 面试题详细答案 - Q17

Q17: Koa.js 和 Express.js 有什么区别?Koa.js 的优势是什么?

Koa.js vs Express.js 对比

1. 基本架构差异
// Express.js - 传统中间件模式
const express = require('express')
const app = express()

app.use((req, res, next) => {
  console.log('中间件 1')
  next()
})

app.use((req, res, next) => {
  console.log('中间件 2')
  next()
})

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

// Koa.js - 洋葱模型
const Koa = require('koa')
const app = new Koa()

app.use(async (ctx, next) => {
  console.log('中间件 1 - 开始')
  await next()
  console.log('中间件 1 - 结束')
})

app.use(async (ctx, next) => {
  console.log('中间件 2 - 开始')
  await next()
  console.log('中间件 2 - 结束')
})

app.use(async (ctx) => {
  ctx.body = 'Hello Koa'
})
2. 错误处理差异
// Express.js 错误处理
app.use((err, req, res, next) => {
  console.error(err.stack)
  res.status(500).send('服务器错误')
})

// Koa.js 错误处理
app.use(async (ctx, next) => {
  try {
    await next()
  } catch (err) {
    ctx.status = err.status || 500
    ctx.body = err.message
    ctx.app.emit('error', err, ctx)
  }
})

Koa.js 核心特性

1. 洋葱模型中间件
const Koa = require('koa')
const app = new Koa()

// 洋葱模型执行顺序
app.use(async (ctx, next) => {
  console.log('1. 外层中间件开始')
  await next()
  console.log('1. 外层中间件结束')
})

app.use(async (ctx, next) => {
  console.log('2. 中层中间件开始')
  await next()
  console.log('2. 中层中间件结束')
})

app.use(async (ctx, next) => {
  console.log('3. 内层中间件开始')
  ctx.body = 'Hello Koa'
  console.log('3. 内层中间件结束')
})

// 执行顺序:
// 1. 外层中间件开始
// 2. 中层中间件开始
// 3. 内层中间件开始
// 3. 内层中间件结束
// 2. 中层中间件结束
// 1. 外层中间件结束
2. Context 对象
const Koa = require('koa')
const app = new Koa()

app.use(async (ctx) => {
  // 请求信息
  console.log('请求方法:', ctx.method)
  console.log('请求URL:', ctx.url)
  console.log('请求头:', ctx.headers)
  console.log('查询参数:', ctx.query)
  console.log('请求体:', ctx.request.body)

  // 响应设置
  ctx.status = 200
  ctx.type = 'application/json'
  ctx.body = {
    message: 'Hello Koa',
    method: ctx.method,
    url: ctx.url,
  }
})
3. 异步支持
const Koa = require('koa')
const app = new Koa()

// 异步中间件
app.use(async (ctx, next) => {
  const start = Date.now()
  await next()
  const ms = Date.now() - start
  ctx.set('X-Response-Time', `${ms}ms`)
})

app.use(async (ctx, next) => {
  const start = Date.now()
  await next()
  const ms = Date.now() - start
  console.log(`${ctx.method} ${ctx.url} - ${ms}ms`)
})

app.use(async (ctx) => {
  // 模拟异步操作
  await new Promise((resolve) => setTimeout(resolve, 100))
  ctx.body = 'Hello Koa'
})

Koa.js 优势

1. 更好的异步支持
// Koa.js 原生支持 async/await
app.use(async (ctx, next) => {
  try {
    // 异步操作
    const data = await fetchDataFromDatabase()
    ctx.body = data
  } catch (error) {
    ctx.status = 500
    ctx.body = { error: error.message }
  }
})

// Express.js 需要额外处理
app.use((req, res, next) => {
  fetchDataFromDatabase()
    .then((data) => {
      res.json(data)
    })
    .catch((error) => {
      res.status(500).json({ error: error.message })
    })
})
2. 更简洁的 API
// Koa.js - 简洁的 API
app.use(async (ctx) => {
  ctx.status = 200
  ctx.body = { message: 'Hello Koa' }
})

// Express.js - 相对复杂
app.use((req, res) => {
  res.status(200).json({ message: 'Hello Express' })
})
3. 更好的错误处理
// Koa.js 错误处理
app.use(async (ctx, next) => {
  try {
    await next()
  } catch (err) {
    ctx.status = err.status || 500
    ctx.body = {
      error: err.message,
      stack: process.env.NODE_ENV === 'development' ? err.stack : undefined,
    }
  }
})

// 抛出错误
app.use(async (ctx) => {
  if (ctx.query.error) {
    const error = new Error('测试错误')
    error.status = 400
    throw error
  }
  ctx.body = '正常响应'
})

实际应用示例

1. 基本 Web 服务器
const Koa = require('koa')
const Router = require('@koa/router')
const app = new Koa()
const router = new Router()

// 中间件
app.use(async (ctx, next) => {
  console.log(`${ctx.method} ${ctx.url}`)
  await next()
})

// 路由
router.get('/', async (ctx) => {
  ctx.body = 'Hello Koa'
})

router.get('/users', async (ctx) => {
  ctx.body = [
    { id: 1, name: 'John' },
    { id: 2, name: 'Jane' },
  ]
})

router.post('/users', async (ctx) => {
  const { name, email } = ctx.request.body
  ctx.status = 201
  ctx.body = { message: '用户创建成功', user: { name, email } }
})

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

app.listen(3000, () => {
  console.log('服务器运行在端口 3000')
})
2. 中间件组合
const Koa = require('koa')
const app = new Koa()

// 日志中间件
app.use(async (ctx, next) => {
  const start = Date.now()
  await next()
  const ms = Date.now() - start
  console.log(`${ctx.method} ${ctx.url} - ${ms}ms`)
})

// 认证中间件
app.use(async (ctx, next) => {
  const token = ctx.headers.authorization
  if (!token) {
    ctx.status = 401
    ctx.body = { error: '未提供认证令牌' }
    return
  }

  // 验证令牌
  if (token === 'valid-token') {
    ctx.user = { id: 1, name: 'John' }
    await next()
  } else {
    ctx.status = 401
    ctx.body = { error: '无效的认证令牌' }
  }
})

// 业务逻辑
app.use(async (ctx) => {
  ctx.body = {
    message: '受保护的内容',
    user: ctx.user,
  }
})
3. 错误处理
const Koa = require('koa')
const app = new Koa()

// 全局错误处理
app.on('error', (err, ctx) => {
  console.error('服务器错误:', err)
})

// 错误处理中间件
app.use(async (ctx, next) => {
  try {
    await next()
  } catch (err) {
    ctx.status = err.status || 500
    ctx.body = {
      error: err.message,
      status: ctx.status,
    }

    // 触发错误事件
    ctx.app.emit('error', err, ctx)
  }
})

// 业务逻辑
app.use(async (ctx) => {
  if (ctx.query.throw) {
    const error = new Error('测试错误')
    error.status = 400
    throw error
  }

  ctx.body = '正常响应'
})

性能对比

1. 内存使用
// Koa.js - 更少的内存使用
const Koa = require('koa')
const app = new Koa()

app.use(async (ctx, next) => {
  // 更少的对象创建
  await next()
})

// Express.js - 更多的内存使用
const express = require('express')
const app = express()

app.use((req, res, next) => {
  // 更多的对象创建
  next()
})
2. 执行速度
// Koa.js - 更快的执行速度
app.use(async (ctx, next) => {
  const start = Date.now()
  await next()
  const ms = Date.now() - start
  console.log(`执行时间: ${ms}ms`)
})

// Express.js - 相对较慢
app.use((req, res, next) => {
  const start = Date.now()
  next()
  const ms = Date.now() - start
  console.log(`执行时间: ${ms}ms`)
})

总结

  • 架构差异:Koa.js 使用洋葱模型,Express.js 使用传统中间件模式
  • 异步支持:Koa.js 原生支持 async/await,Express.js 需要额外处理
  • API 设计:Koa.js API 更简洁,Express.js 相对复杂
  • 错误处理:Koa.js 错误处理更优雅,Express.js 需要额外配置
  • 性能优势:Koa.js 内存使用更少,执行速度更快
  • 学习曲线:Koa.js 学习曲线较陡,Express.js 更容易上手
  • 生态系统:Express.js 生态系统更成熟,Koa.js 相对较新
  • 适用场景:Koa.js 适合现代异步应用,Express.js 适合传统 Web 应用