今年3月,可以说是2020年的“新”起点,趁着疫情期间,对过往和未来技术方向做个小整理。

Express 几乎与 Node.js 一样老,也是目前最流行的 Node.js web应用框架,很多应用框架也是基于 Express 构建。Koa 则属于后起之秀,近年来在国内比较热门且主流的框架之一。两者都可轻松的创建 REST APIs、静态服务器等,本文主要就拿这两个框架进行对比。

当然还有:HapiEgg.jsNest.js 等框架,在文章结尾会简单介绍,由于并为实际使用,所以不在本文讨论范围,仅作为扩展内容去理解。

基本介绍:

Express:

Fast, unopinionated, minimalist web framework for node.(高度包容、快速而极简的 Node.js Web 框架。)

Express 是针对 Node.js 的Web应用框架。为 Web 和移动应用程序提供一组强大的功能,并保持最低程度规模的灵活。
由 TJ Holowaychuk 创立,首次提交于 2010年第二季度,2014年被 StrongLoop 收购,2016年 StrongLoop 又被 IBM 并购,目前置于 Node.js 基金会孵化器的管理之下。
Express 内置路由和模版,开箱即用。

Koa:

Expressive middleware for node.js using ES2017 async functions.(使用 ES2017 异步函数处理 Node.js 中间件。)

Koa 由 Express 同一团队开发,作为下一代 Web 框架,更现代和轻量。
首次提交于 2013年底,更为年轻,支持更现代的 ES6/7/8 语法。
Koa 不提供内置路由等和任何中间件,更加简化。

因为使用 ES6 特性,所以需要 Node v7.6.0+ 版本的支持。

数据统计:

以下数据,基于 2020-02-29 各自的 NPM 项目主页。

Express:

  • Current Version: 4.17.1
  • Weekly Downloads: 11,886,941
  • Github Stars: 47.5k
  • Unpacked Size: 208 kB
  • Last publish: 9 months ago

Koa:

  • Current Version: 2.11.0
  • Weekly Downloads: 409,189
  • Github Stars: 28.6k
  • Unpacked Size: 83.8 kB
  • Last publish: 4 months ago

基础示例:

开发 Node.js 的应用程序,第一步就是创建一个最基本的服务器。

Express:

const express = require('express')
const app = express()
app.listen(3000, () => console.log('App is listening on port 3000!'))

此处的 app.listen() 其实是 http.createServer() 的语法糖。

Koa:

const Koa = require('koa')
const app = new Koa()
app.listen(3000, () => console.log('App is listening on port 3000!'))

Koa 的 app.listen() 同样也是 http.createServer() 的语法糖。但请注意第一行的 Koa 是大写,因为导入的是一个 class 类。

简述:

从中可以发现 Koa 与 Express 十分相似,基础用法几乎一样。

中间件:

中间件是位于 请求对象(Request)响应对象(Response) 之间的函数。next() 可以放行中间件,允许执下一条命令,否则当前中间件函数没有 结束请求/响应循环 时,那么请求将保持 挂起状态,导致后续命令无法执行

Express:

const express = require('express')
const app = express()
app.use((req, res, next) => {
  console.log(`Time: ${Date.now()}`)
  next()
})
app.listen(3000)

通过 app.use() 注册,将中间件绑定到 app 对象。

Koa:

const Koa = require('koa')
const app = new Koa()
app.use(async (ctx, next) => {
  console.log(`Time: ${Date.now()}`)
  await next()
})
app.listen(3000)

中间件注册类似 Express,但增加了 上下文对象(Context),可以把单次请求相关的上下文全挂载上去,比 请求对象(Request)响应对象(Response) 更加的语义化,对于任何请求也都将用更现代的 async 函数。

简述:

两者的核心中间件,分别是:compressionkoa-compress ,通过源码可以发现 Koa 通过 Promise 方法更为精简,减少了回调的处理。在 Koa 中,一切流程都是中间件,数据流向遵循洋葱模型,类似堆栈的先入后出原则。

基本路由:

路由是服务器的重要特性之一,首先为两个框架创建一个老套的 "Hello world" 应用程序。

Express:

const express = require('express')
const app = express()
const router = express.Router()
router.get('/hello', (req, res, next) => {
  res.send('Hello World!')
})
router.get('/hello/:name', (req, res, next) => {
  let name = req.params.name
  res.send(`Hello, ${name}!`)
})
app.use('/', router)
app.listen(3000, () => {
  console.log(`listening on *: ${3000}`)
})

Express 可以使用 app.get() 方法来获取 GET 参数,其语法为 app.METHOD(PATH, HANDLER)。通过回调函数来处理 请求(req)响应(res)。其内置的路由方法,还包括 app.post()app.delete()等。

但更推荐使用内置的 express.Router ,可以将中间件与路由进行分离,能为不同应用内置一个单独的应用路由。

Koa:

const Koa = require('koa')
const Router = require('koa-router')
const app = new Koa()
const router = new Router()
router.get('/hello', (ctx, next) => {
  ctx.body = 'Hello World!'
})
router.get('/hello/:name', (ctx, next) => {
    let name = ctx.params.name;
    ctx.response.body = `Hello, ${name}!`
});
app.use(router.routes())
app.listen(3000)

Koa 与 Express 略有不同,因为没有内置任何中间件,需要额外安装 koa-router 依赖。

简述:

两者设计风格非常接近,底层也都是共用的同一套 HTTP 基础库

POST请求和响应

POST 请求也是最常用的请求之一,两者分别需要额外安装 body-parserkoa-bodyparser 依赖,对请求体进行解析。

Express:

const express = require('express')
const bodyParser = require('body-parser')
const app = express()
const router = express.Router()
app.use(bodyParser())
router.post('/api', async (req, res, next) => {
  try {
    await next()
    res.json(req.body)
  } catch (error) {
    res.json(error)
  }
})
app.use('/', router)
app.listen(3000)

Koa:

const Koa = require('koa')
const Router = require('koa-router')
const bodyParser = require('koa-bodyparser')
const app = new Koa()
const router = new Router()
app.use(bodyParser())
router.post('/api', async (ctx, next) => {
  try {
    await next()
    ctx.response.body = ctx.request.body
  } catch (error) {
    ctx.response.body = error
  }
})
app.use(router.routes())
app.listen(3000)

简述

两个的请求地址同为 http://localhost:3000/api/。同时可以发现 Express 也可以支持 async/await 函数,因为 Node.js 7+ 已经原生支持,也能有效的避免地狱回调

现在 Node 已经 12.x,对 ES 的支持度也提高不少。各版本具体对 ES6 的支持程度,可在此查看:https://node.green/

优点:

Express:

  • 简单且快速的开发应用。
  • 拥有庞大的生态,支持更多的第三方插件。
  • 社区更加成熟,意味着有更多支持与文档可以查阅。
  • 学习曲线低。

Koa:

  • 更轻量,更健壮。
  • 对 ES2017 的支持。
  • 可以自己开发中间件,更灵活,自由度高。
  • 代码可读性和维护性更高。

缺点:

Express:

  • 需要清晰的组织结构,否则后期维护难度困难。
  • 代码库越大,重构难度越大。
  • 需要创建所有节点。
  • 没有内置错误处理。

Koa:

  • 社区相对较小,意味着支持与文档也会较少。
  • 与 Express 风格的中间件不兼容。
  • Koa 的特定中间件与其他框架不兼容。

总结:

从 17 年开始,就有 Koa 何时会超越 Express 的讨论,虽然 Koa 确实在迅速增长,就目前看来还是有很长一段路要走。

最初 Express 由于不支持 ES6,地狱回调问题让人着实头疼,但随着 Node 版本更新,对 ES 的支持度提升,两者的差距也在缩小。另外, Express 5.0 alpha 第一版已经发布,对部分 API 进行了调整,但就更新频率来看,Koa 明显要大于 Express。

Express 对中小型项目来说,仍然是个不错的选择,对大型项目来说需要前期良好的规划和模块化,否则管理起来难度会越来越大。

Koa 更适合初学者,是一个轻量化的基础框架,可以从零开始搭建,代码管理更为容易,错误处理起来更方便,适合敏捷开发。

最终的选择,还是一贯策略,取决于项目需求和团队情况。

扩展:

Hapi
由 Walmart Labs 团队开发,最初该团队是基于 Express,但由于无法满足他们的后续需求之后,便开发了此框架(Github Stars: 12.1k,Weekly Downloads: 250,000+)。

Egg.js
由阿里基于 Koa 开发的框架,在 Koa 的模型基础上,进一步对它进行了一些增强(Github Stars: 28.6k,Weekly Downloads: 5,000+)。

Nest.js
渐进式 Node.js 框架,可以在TypeScript和JavaScript (ES6、ES7、ES8)之上高效构建。在 2019年 JavaScript 明星项目 Node.js 框架组内,点赞增长数位列第一(Github Stars: 24.7k,Weekly Downloads: 70,000+)。

参考文章:

Koa vs Express
Is Express the Best Option?
Node.js Framework Comparison: Express vs. Koa vs. Hapi
Choosing the right Node.js Framework: Express, Koa, or Hapi?
A Side-by-side Comparison of Express, Koa and Hapi.js

感谢您的阅读,本文由 蓝色梦想 版权所有。如若转载,请注明出处:蓝色梦想 - Express 和 Koa 框架对比