对比下 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 |
代码比较
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"
}
依赖情况总结
从上图看
- Nest 是依赖于 Express
- Midway 依赖于 Egg,Egg 又依赖于 Koa
## Hello World 对比 
下面分别展示不同框架下实现 `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
- goldbergyoni/nodebestpractices
- expressjs/express
- gofiber/fiber
- nswbmw/N-blog
- goldbergyoni/javascript-testing-best-practices
- linnovate/mean
- apollographql/apollo-server
- bailicangdu/node-elm
- graphile/postgraphile
- microsoft/TypeScript-Node-Starter
Koa
- strapi/strapi
- koajs/koa
- eggjs/egg
- apollographql/apollo-server
- graphile/postgraphile
- aui/art-template
- r-spacex/SpaceX-API
- chenshenhai/koa2-note
- berwin/Blog
- simov/grant
### Egg
- eggjs/egg
- eggjs/examples
- atian25/blog
- cnodejs/egg-cnode
- easy-team/egg-vue-webpack-boilerplate
- wangweianger/zanePerfor
- DXY-F2E/api-mocker
- cool-team-official/cool-admin-midway
- eggjs/awesome-egg
- parkervcp/eggs
Midway
- midwayjs/midway
- midwayjs/pandora
- cool-team-official/cool-admin-midway
- midwayjs/hooks
- midwayjs/midway-faas
- fsd-nodejs/service-mw2
- midwayjs/midway-examples
- atzcl/z
- hackycy/sf-vue-admin
- emilianoarlettaz/arcade-pcb-case
### Nest
- nestjs/nest
- CCOSTAN/Home-AssistantConfig
- elastic/elasticsearch-net
- notadd/notadd
- nestjsx/crud
- nestjs/nest-cli
- nestjs/typescript-starter
- nestjs/typeorm
- surmon-china/nodepress
## 微服务 支持 | FrameWork | 微服务 支持度 | | --- | --- | | Express | ⭐️ ⭐️ | | Koa | ⭐️ ⭐️ | | Egg | ⭐️ ⭐️ | | Midway | ⭐️ ⭐️ ⭐️ ⭐️ ⭐️ | | Nest | ⭐️ ⭐️ ⭐️ ⭐️ ⭐️ |