对比对比Node的Web框架

对比下 node 生态下的 web 框架。

主要从以下几个方面进行对比

  • github watch star fork
  • 主要实现
FrameWork Github Docs Watch Star Fork
express 地址 地址 1.8k 54.5k 9.2k
koa 地址 地址 853 31.7k 3.1k
egg 地址 地址 484 17.3k 1.7k
midway 地址 地址 91 4.7k 281
nest 地址 地址 679 40.5k 4.1k

代码比较

image.png

git clone https://github.com/expressjs/express.git;
git clone https://github.com/koajs/koa.git;
git clone https://github.com/eggjs/egg.git;
git clone https://github.com/midwayjs/midway.git;
git clone https://github.com/nestjs/nest.git;
复制代码

拉取 5 个项目的源代码,利用 Vscode Statistics 对代码行数进行统计。

Express

language files code comment blank total
JavaScript 152 14,018 3,033 3,717 20,768
Markdown 12 3,648 0 844 4,492
HTML 28 260 0 53 313
YAML 3 174 19 31 224
JSON 1 99 0 1 100
Handlebars 3 71 0 8 79
CSS 4 44 0 3 47
Makefile 1 11 0 3 14

Koa

language files code comment blank total
JSON 2 11,097 0 2 11,099
JavaScript 74 4,533 1,116 1,197 6,846
Markdown 13 2,197 2 819 3,018
YAML 6 47 0 12 59

Egg

language files code comment blank total
Markdown 104 23,298 30 7,704 31,032
JavaScript 475 11,514 1,491 2,459 15,464
TypeScript 25 1,065 691 281 2,037
JSON 140 610 0 132 742
YAML 10 288 14 25 327
PlantUML 2 288 0 2 290
HTML 17 44 0 14 58
Shell Script 2 22 8 10 40
Properties 1 1 0 1 2
XML 1 1 0 0 1

Midway

language files code comment blank total
Markdown 52 5,103 969 6,369 12,441
TypeScript 231 4,453 476 1,123 6,052
JavaScript 117 2,689 166 550 3,405
JSON 100 1,859 948 98 2,905
YAML 8 131 24 30 185
Shell Script 7 85 7 14 106
TypeScript React 3 49 1 6 56
XML 2 30 0 2 32
HTML 2 18 0 4 22
CSS 8 15 0 9 24
Ignore 1 1 0 0 1

Nest

language files code comment blank total
JSON 232 353,573 268 136 353,977
TypeScript 1,288 57,715 4,935 8,326 70,976
JavaScript 53 5,642 2,321 1,366 9,329
Markdown 45 2,103 97 878 3,078
YAML 14 362 47 52 461
graphql 7 330 0 95 425
HTML 4 60 0 6 66
XML 1 43 0 1 44
Handlebars 2 20 0 6 26
Shell Script 5 16 9 5 30
Ignore 1 2 0 0 2

代码统计小结

上面只是一个简单的统计,只能反应一些大致情况。

依赖情况 package.json

Express

{
	  "accepts": "~1.3.7",
    "array-flatten": "1.1.1",
    "body-parser": "1.19.0",
    "content-disposition": "0.5.3",
    "content-type": "~1.0.4",
    "cookie": "0.4.0",
    "cookie-signature": "1.0.6",
    "debug": "2.6.9",
    "depd": "~1.1.2",
    "encodeurl": "~1.0.2",
    "escape-html": "~1.0.3",
    "etag": "~1.8.1",
    "finalhandler": "~1.1.2",
    "fresh": "0.5.2",
    "merge-descriptors": "1.0.1",
    "methods": "~1.1.2",
    "on-finished": "~2.3.0",
    "parseurl": "~1.3.3",
    "path-to-regexp": "0.1.7",
    "proxy-addr": "~2.0.5",
    "qs": "6.7.0",
    "range-parser": "~1.2.1",
    "safe-buffer": "5.1.2",
    "send": "0.17.1",
    "serve-static": "1.14.1",
    "setprototypeof": "1.1.1",
    "statuses": "~1.5.0",
    "type-is": "~1.6.18",
    "utils-merge": "1.0.1",
    "vary": "~1.1.2"
}
复制代码

Koa

{
    "accepts": "^1.3.5",
    "cache-content-type": "^1.0.0",
    "content-disposition": "~0.5.2",
    "content-type": "^1.0.4",
    "cookies": "~0.8.0",
    "debug": "^4.3.2",
    "delegates": "^1.0.0",
    "destroy": "^1.0.4",
    "encodeurl": "^1.0.2",
    "escape-html": "^1.0.3",
    "fresh": "~0.5.2",
    "http-assert": "^1.3.0",
    "http-errors": "^1.6.3",
    "koa-compose": "^4.1.0",
    "on-finished": "^2.3.0",
    "only": "~0.0.2",
    "parseurl": "^1.3.2",
    "statuses": "^1.5.0",
    "type-is": "^1.6.16",
    "vary": "^1.1.2"
}
复制代码

Egg

{
    "@types/accepts": "^1.3.5",
    "@types/koa": "^2.0.48",
    "@types/koa-router": "^7.0.40",
    "accepts": "^1.3.5",
    "agentkeepalive": "^4.0.2",
    "cache-content-type": "^1.0.1",
    "circular-json-for-egg": "^1.0.0",
    "cluster-client": "^3.0.1",
    "debug": "^4.1.1",
    "delegates": "^1.0.0",
    "egg-cluster": "^1.23.0",
    "egg-cookies": "^2.3.0",
    "egg-core": "^4.18.0",
    "egg-development": "^2.4.2",
    "egg-i18n": "^2.0.0",
    "egg-jsonp": "^2.0.0",
    "egg-logger": "^2.3.2",
    "egg-logrotator": "^3.0.5",
    "egg-multipart": "^2.4.0",
    "egg-onerror": "^2.1.0",
    "egg-schedule": "^3.6.0",
    "egg-security": "^2.4.3",
    "egg-session": "^3.1.0",
    "egg-static": "^2.2.0",
    "egg-view": "^2.1.2",
    "egg-watcher": "^3.1.0",
    "extend2": "^1.0.0",
    "graceful": "^1.0.2",
    "humanize-ms": "^1.2.1",
    "is-type-of": "^1.2.1",
    "koa-bodyparser": "^4.2.1",
    "koa-is-json": "^1.0.0",
    "koa-override": "^3.0.0",
    "ms": "^2.1.1",
    "mz": "^2.7.0",
    "on-finished": "^2.3.0",
    "semver": "^7.3.2",
    "sendmessage": "^1.1.0",
    "urllib": "^2.33.0",
    "utility": "^1.15.0",
    "ylru": "^1.2.1"
  }
复制代码

Midway

{
    "@eggjs/router": "^2.0.0",
    "@midwayjs/decorator": "^1.20.3",
    "debug": "^4.1.1",
    "egg": "^2.20.0",
    "egg-core": "^4.15.0",
    "egg-logger": "^2.3.2",
    "extend2": "^1.0.0",
    "injection": "^1.8.0",
    "midway-core": "^1.20.3",
    "midway-schedule": "^1.20.3",
    "mkdirp": "^0.5.1"
  }
复制代码

Nest

{
    "@nuxtjs/opencollective": "0.3.2",
    "axios": "0.21.1",
    "class-transformer": "0.4.0",
    "class-validator": "0.13.1",
    "cli-color": "2.0.0",
    "cors": "2.8.5",
    "express": "4.17.1",
    "fast-json-stringify": "2.7.9",
    "fast-safe-stringify": "2.0.8",
    "iterare": "1.2.1",
    "object-hash": "2.2.0",
    "path-to-regexp": "3.2.0",
    "reflect-metadata": "0.1.13",
    "rxjs": "7.3.0",
    "socket.io": "4.1.3",
    "tslib": "2.3.1",
    "uuid": "8.3.2"
}
复制代码

依赖情况总结

image.png
从上图看

  • Nest 是依赖于 Express
  • Midway 依赖于 Egg,Egg 又依赖于 Koa

## Hello World 对比
![image.png](https://cdn.nlark.com/yuque/0/2021/png/750901/1631446783578-51fe6474-6bae-4579-9e9f-167a3c77e791.png#clientId=u851b1f9b-9a9b-4&from=paste&height=188&id=u1bc00bd8&margin=%5Bobject%20Object%5D&name=image.png&originHeight=376&originWidth=800&originalType=binary&ratio=1&size=248257&status=done&style=none&taskId=ud69a0a72-1019-46b8-8100-974ef0cb9e5&width=400)
下面分别展示不同框架下实现 `helloword`

### Express
`pre install`
```json
mkdir express; cd express; npm init -y; yarn add express ; touch app.js;
```
`app.js`
```json
const express = require('express')
const app = express()
const port = 3000

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

app.listen(port, () => {
console.log(Example app listening at http://localhost:${port})
})

`run`

- node app.js
- curl http://localhost:3000



目录情况
```json
|____package.json
|____app.js
复制代码

Koa

  • pre install -> mkdir koa; cd koa; npm init -y; yarn add koa; vim app.js
  • 输入 app.js
const Koa = require('koa');
const app = new Koa();

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

app.listen(3000);
复制代码
  • node app.js && curl localhost:3000

目录

|____package.json
|____app.js

复制代码

### Egg

  • pre install -> mkdir egg; cd egg; npm init egg --type=simple; npm i;
  • npm run dev && curl localhost:7001

目录

.
|____jsconfig.json
|____app
| |____controller
| | |____home.js
| |____router.js
| |____public
|____test
|____config
| |____config.default.js
| |____plugin.js
|____.eslintrc
|____.autod.conf.js
|____README.md
|____appveyor.yml
|____logs
|____.gitignore
|____package.json
|____.github
| |____workflows
| | |____nodejs.yml
|____.eslintignore
|____run
|____.travis.yml

复制代码

如果说 express 和 koa 给我们的是一张白纸,egg 就为我们约定好了一套企业通用的规则。

Midway

  • pre install -> npm init midway --type=web midway;
  • npm run dev; curl localhost;7001

目录

.
|____test
|____jest.setup.js
|____jest.config.js
|____bootstrap.js
|____typings
|____.editorconfig
|____logs
|____.gitignore
|____package.json
|____tsconfig.json
|____run
|____.eslintrc.json
|____src
| |____config
| | |____config.local.ts
| | |____plugin.ts
| | |____config.default.ts
| | |____config.unittest.ts
| |____controller
| | |____api.ts
| | |____home.ts
| |____interface.ts
| |____service
| | |____user.ts
| |____configuration.ts

复制代码

其他的语言生成的都是 JavaScript,Midway 生成的默认语法 TypeScript。而且从上面的结构上看 MidWay 生成的结构和 egg 的结构非常像,所以说底层应该是引用了 Egg 的。

### Nest

  • pre install -> npm i -g @nestjs/cli; nest new nest;
  • yarn run start; curl localhost:3000

目录

.
|____test
| |____app.e2e-spec.ts
| |____jest-e2e.json
|____nest-cli.json
|____README.md
|____yarn.lock
|____.gitignore
|____package.json
|____tsconfig.build.json
|____.prettierrc
|____.eslintrc.js
|____tsconfig.json
|____src
| |____main.ts
| |____app.service.ts
| |____app.module.ts
| |____app.controller.spec.ts
| |____app.controller.ts

复制代码

nest 默认也是 TypeScript

TypeScript 的支持

FrameWork TypeScript 支持度
Express ⭐️ ⭐️ ⭐️
Koa ⭐️ ⭐️ ⭐️
Egg ⭐️ ⭐️
Midway ⭐️ ⭐️ ⭐️ ⭐️ ⭐️
Nest ⭐️ ⭐️ ⭐️ ⭐️ ⭐️

因为 Egg 的核心是 loader 加载机制。更多的是约定大于配置,这样对 TypeScript 的支持我个人感觉更加不友好 😈

相关项目

语言的未来的发展。这里挑选的是在 Github 中 topic:${name} 的前 Top 10 的项目。

Express

Koa

### Egg

Midway

### Nest

## 微服务 支持
| FrameWork | 微服务 支持度 |
| --- | --- |
| Express | ⭐️ ⭐️ |
| Koa | ⭐️ ⭐️ |
| Egg | ⭐️ ⭐️ |
| Midway | ⭐️ ⭐️ ⭐️ ⭐️ ⭐️ |
| Nest | ⭐️ ⭐️ ⭐️ ⭐️ ⭐️ |