Node.js 面试题详细答案 - Q17
Q17: Koa.js 和 Express.js 有什么区别?Koa.js 的优势是什么?
Koa.js vs Express.js 对比
1. 基本架构差异
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')
})
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. 错误处理差异
app.use((err, req, res, next) => {
console.error(err.stack)
res.status(500).send('服务器错误')
})
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. 内层中间件结束')
})
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. 更好的异步支持
app.use(async (ctx, next) => {
try {
const data = await fetchDataFromDatabase()
ctx.body = data
} catch (error) {
ctx.status = 500
ctx.body = { error: error.message }
}
})
app.use((req, res, next) => {
fetchDataFromDatabase()
.then((data) => {
res.json(data)
})
.catch((error) => {
res.status(500).json({ error: error.message })
})
})
2. 更简洁的 API
app.use(async (ctx) => {
ctx.status = 200
ctx.body = { message: 'Hello Koa' }
})
app.use((req, res) => {
res.status(200).json({ message: 'Hello Express' })
})
3. 更好的错误处理
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. 内存使用
const Koa = require('koa')
const app = new Koa()
app.use(async (ctx, next) => {
await next()
})
const express = require('express')
const app = express()
app.use((req, res, next) => {
next()
})
2. 执行速度
app.use(async (ctx, next) => {
const start = Date.now()
await next()
const ms = Date.now() - start
console.log(`执行时间: ${ms}ms`)
})
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 应用