【egg已经是过街老鼠了吗?】首选ts版本midway。我能说的它们是经过阿里业务严重过,双十一,还有蚂蚁的ssr,量大到全网最大node集群,这个说法不过分。
稳,可扩展,生态好,出了问题自己努努力还是能搞定的。
如果非要ts,可以考虑midway或daruk,一个是egg生态加强版,一个基于koa小到刚好的。都不错。
nest或next或nuxt这种,sloc试试就知道了。太重,无大佬慎用。文档也比egg差一大截。
其实fastify,blitz也是可以考虑的。
star数不能说明质量一定好,它只是同等情况下,star数高更好。比如express在2010年就有了,曾经的王者,一直有很高的用户量,star数高很正常。
nest是很像spring,但实话讲,没学到精髓。我其实曾去研究过javaee规范和Tomcat原理,也用过ssh1和2,和spring boot。node其实简化这一套,无论容器,上下文,connector,都没了。那nest是啥?Tomcat+spring?
- 一个框架是否轻量级不是看他的代码库有多大,而是看他复用你原有的代码的能力强不强,是否需要写框架专用代码。很明显 egg 需要写一堆专用代码才能利用 koa 的中间件,所以 egg 不轻量级。
koa一共5/600行代码,最多为一个中间件内核。它不内置一个中间件,它肯定比egg轻量。
egg核心是面向架构设计,做了大量约定和抽象,使用koa中间件代码可以直接用,只需要按照egg写法就好了,有很复杂?好好看看文档
https://eggjs.org/zh-cn/intro/egg-and-koa.htmleggjs.org/zh-cn/intro/egg-and-koa.html
- 一个框架是否设计的漂亮,需要看他如何适配外部的代码,和如何安排内置的功能。很明显一个 httpclient 绑在 context 上的框架设计并不能说设计的漂亮。context 是啥?请求上下文啊,把 httpclient 绑在 context 上你想干嘛?
不理解啥时候bff吧,去看看
- ctx是个垃圾堆,啥都往里面堆,到后面你都不知道自己往 ctx 里面写了什么东西,此之谓全局命名空间污染。
这其实一个妥协,ctx和express req一样,用多了都恶心,中间件机制在这里,没有更好的方式,有更好解法吗?
- service 没有生命周期,这个东西导致了 ctx 和 app 变成了垃圾堆。
去看看Java里,dao层和service层的关系,理解service为业务逻辑,为啥需要生命周期呢?没看懂。
- 一个东西经受住双十一的考验不代表他是一个好东西,因为 egg 的设计真的垃圾。
从n个bu,n个框架抽象出来的,到底哪里垃圾,貌似你没有讲出来。
- 你可能认为你拿 egg 很快写上一个 web ,但实际 egg 已经带偏了你的认知,不如直接上 koa/express。
是一个级别吗?egg号称企业级框架,岂是koa和express可比。
- 约定优于配置,配置即代码。实际上 egg 是没地方给你配置,配置脱离代码。要看约定优于配置,配置即代码,可以看看 asp.net core ,为什么是 asp.net core 不是其他?因为我只学过 asp.net core 没学过 spring boot。
约定大于配置起源自ruby on rails,那.net理解,总觉得怪怪的,egg的config目录,没地方配?各种环境抽象,设计的很差?
先好好学学吧。
- 不是你没用依赖注入就是轻量级,实际上依赖注入天生为轻量级而生。依赖注入不仅轻量级,还能让你的代码依赖有迹可寻。不是你不理解的东西就是重量级。相反 ctx 到处绑,这是真的重耦合。
egg出现的时候,ts还没有今天这样成熟,更不要提ioc,本身就是node优势就是极其轻量,一个文件起一个server,纯js写法能实现ioc,但社区和业界并没有很好的实践,我理解这也是一种取舍。egg帮你做了一些约定,但没有完全走ioc,代码依赖有迹可循是有的,一旦习惯egg,效率非常高的。ctx是请求级别的,适当共享一些内容是ok的,看怎么样用。用不好,不代表锤子不好。
egg维护为啥不那么频繁?因为成熟了
ts版本egg有midway,复用生态,不内卷,完美。
Node的web框架也有很多可以选择的,前一阵梳理了一下7类
-
connect/express/restify,是express风格的各种框架
-
koa/egg/midway,基于koa系列的
-
daruk,最“中”的ts框架(不大不小,刚刚好,支持koa,支持ioc)
-
nest,最像java spring的框架,启发自angular
-
fastify 最快的node框架,甚至比node原生的http server还要快
-
nextjs和ykfe/ssr 服务端渲染框架
-
Redwood 基于jamstack理念架构(JavaScript api markup)的框架
-
loopback/meanjs/blitzjs 全栈整合型框架
总结
-
从面相过程到面向对象,使用js或ts,你都可以选择
-
从api到ssr,到传统web开发,甚至是bff,你想要的全都有
-
npm 覆盖1100万开发者,超过100万个模块。所有开源包管理,绝对第一
如果不开心就自己写一个,参考 https://scalatra.org//guides/2.7/http/routes.html。
具体写法如下。
class A {
get(path = "/") {
return '{"message":"hello world"}'
}
}
这样写起来更简单。
第一版代码
const http = require('http')
const router = require('find-my-way')()
const FN_ARGS = /^(function)?\s*\*?\s*[^\(]*\(\s*([^\)]*)\)/m
const FN_ARG_SPLIT = /,/
const FN_ARG = /^\s*(_?)(\S+?)\1\s*$/
const STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg
const cache = {}
function getParameters(fn) {
const fnText = fn.toString();
if (!cache[fnText]) {
const inject = {};
const argDecl = fnText.replace(STRIP_COMMENTS, '').match(FN_ARGS);
// console.log(argDecl)
argDecl[2].split(FN_ARG_SPLIT).forEach(function (arg) {
// console.log(arg)
if (arg.indexOf('=') != -1) {
var i = arg.split("=")
inject[i[0].trim()] = i[1].split('"')[1]
}
arg.replace(FN_ARG, function (all, underscore, name) {
inject[name] = null
});
});
cache[fnText] = inject
}
return cache[fnText]
}
class A {
get(path = "/") {
return '{"message":"hello world"}'
}
}
var a = new A()
var propertyNames = Object.getOwnPropertyNames(Object.getPrototypeOf(a));
console.dir(propertyNames)
for (var i in propertyNames) {
// console.dir(propertyNames[i])
if ('constructor' !== propertyNames[i]) {
// console.dir(propertyNames[i].toUpperCase())
var parameters = getParameters(a[propertyNames[i]])
var path = parameters['path']
// console.dir(b)
var _original = a[propertyNames[i]];
var _newfn = function () {
// console.log('in');
// console.dir(arguments[0])
var result = _original.apply(this, arguments);
// console.log('out');
return result;
}
router.on(propertyNames[i].toUpperCase(), path, (req, res, params) => {
// res.end('{"message":"hello world"}')
a.req = req
a.res = res
var html = _newfn.bind(a)(path, req, res, params);
res.end(html)
})
}
}
const server = http.createServer((req, res) => {
router.lookup(req, res)
})
server.listen(3000, err => {
if (err) throw err
console.log('Server listening on: http://localhost:3000')
})
简单的做了一下benchmark,和find-my-way基本一样,没有性能损耗。
scalatra
➜ ~ autocannon -c 100 -d 5 -p 10 localhost:3000
Running 5s test @ http://localhost:3000
100 connections with 10 pipelining factor
┌─────────┬──────┬───────┬───────┬───────┬──────────┬─────────┬───────┐
│ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │
├─────────┼──────┼───────┼───────┼───────┼──────────┼─────────┼───────┤
│ Latency │ 8 ms │ 12 ms │ 23 ms │ 25 ms │ 12.47 ms │ 4.94 ms │ 92 ms │
└─────────┴──────┴───────┴───────┴───────┴──────────┴─────────┴───────┘
┌───────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┐
│ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
├───────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Req/Sec │ 66559 │ 66559 │ 80767 │ 82111 │ 77612.8 │ 5713.37 │ 66510 │
├───────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Bytes/Sec │ 8.31 MB │ 8.31 MB │ 10.1 MB │ 10.3 MB │ 9.7 MB │ 715 kB │ 8.31 MB │
└───────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┘
Req/Bytes counts sampled once per second.
388k requests in 5.06s, 48.5 MB read
find my way
➜ ~ autocannon -c 100 -d 5 -p 10 localhost:3000
Running 5s test @ http://localhost:3000
100 connections with 10 pipelining factor
┌─────────┬───────┬───────┬───────┬───────┬──────────┬─────────┬───────┐
│ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │
├─────────┼───────┼───────┼───────┼───────┼──────────┼─────────┼───────┤
│ Latency │ 11 ms │ 12 ms │ 19 ms │ 25 ms │ 12.57 ms │ 4.49 ms │ 96 ms │
└─────────┴───────┴───────┴───────┴───────┴──────────┴─────────┴───────┘
┌───────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┐
│ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
├───────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Req/Sec │ 66047 │ 66047 │ 79359 │ 80639 │ 76947.2 │ 5524.71 │ 66010 │
├───────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Bytes/Sec │ 8.25 MB │ 8.25 MB │ 9.92 MB │ 10.1 MB │ 9.62 MB │ 691 kB │ 8.25 MB │
└───────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┘
Req/Bytes counts sampled once per second.
385k requests in 5.06s, 48.1 MB read