今年3月,可以说是2020年的“新”起点,趁着疫情期间,对过往和未来技术方向做个小整理。
Express 几乎与 Node.js 一样老,也是目前最流行的 Node.js web应用框架,很多应用框架也是基于 Express 构建。Koa 则属于后起之秀,近年来在国内比较热门且主流的框架之一。两者都可轻松的创建 REST APIs、静态服务器等,本文主要就拿这两个框架进行对比。
当然还有:
Hapi
、Egg.js
、Nest.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
函数。
简述:
两者的核心中间件,分别是:compression 和 koa-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-parser
和 koa-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