天冷了,唯有学习来温暖自己。
最近利用业余的时间,跟着 coderwhy 老师学习 node.js,了解以及掌握一些服务端的常见知识:
确实学习到了很多东西,填充了自己的知识体系的未知领域。
node.js 也许是前端开发者踏入服务端开发的最好选择。同样的 JavaScript,同样的语法,以及同样的你,基本上可以达到无缝衔接的开发。
对于 node.js 而言,社区里面出现了非常多的框架,快速上手,敏捷开发。
koa 和 express 就是其中的比较两个突出的框架。
在阅读下文之前,希望你掌握了 express 和 koa 的基本使用,不然下面内容对你的帮助也许不是那么的大。
express 是一个基于 node.js 平台,快速,开放,极简的 web 开发框架。express 官网
koa 是基于 node.js 平台的下一代 web 开发框架。koa 官网
两个框架都是同一个团队开发的,都是 node.js 中比较优秀的框架。
都是 node.js 框架,同一个团队为什么要开发两个呢?
express 也许是 node.js 最早出的框架,里面集成了大量的中间件,造成了框架的笨重性。团队领队人 TJ 发现了 express 的设计是有缺陷的,如果想要修复这个设计缺陷,就会造成 express 的框架重构。
基于上面的种种原因(当然还有不知道的),就打算重新写一个新的框架->koa,来实现 express 的不足之处。
express 现在有团队中的团员维护,基本上也很少更新了。
koa 由团队领队人 TJ 主要维护。
--来自 coderwhy 老师的闲谈
上面的闲谈的内容不重要,当个乐子开心一下就好了。
一起看看 express 和 koa 在 github 上的星标:

可以发现 express 的使用率还是比 koa 高,尽管 express 笨重,设计有缺陷,但是对于开发者而言,当不考虑这些因素情况下,express 还是吃香的。
两者都是同一团队写的两个框架,那么核心的思想肯定是相同的,当然肯定会也存在差异,那么接下来就来重点比较一下其中的差异吧。
下面主要从两个方面来分析其中的差异:设计架构、中间件。
因为也是第一次接触 koa (express 以前是接触了的),如果存在有误的地方,请指出来,虚心受教,共同进步。
express 是完整和强大的,里面集成了大量的中间件,比如说:路由,静态资源等中间件。对于开发者而言,技术统一,都是使用 express 内部提供的。

koa 是灵活和自由的,基本上没有集成任何中间件(核心代码只有大致1600行左右),中间件都需要自己去安装。对于开发者而言,选择技术的类型是多样的,丰富的。

webstorm 和 vscode 的使用,都是因人而异,没有谁强谁弱。那么对于 express 和 koa 也是同样的道理,各自有各自优势。
接下来我们一起来听听 express 和 koa 独白:
express:hi,koa,我俩同处一体,我俩比比?
koa:???
express:我俩的发动机都是中间件,我有三个兄弟:request,response,next,你有几个兄弟呢?
koa:额,我有两个兄弟:context,next。不过我这 context 兄弟很厉害,以一敌二(request,response)。
express:我自带路由(Router),直接使用即可,你呢?
koa:安装。(koa-router 或者 @dva/router)
express:我可以自己暴露静态资源,你呢?
koa:安装。(koa-static)
express:我只需要配置一下,就可以解析客户端传递 application/json 的格式数据,你呢?
koa:还是安装。(koa-bodyparser)
express:你能不能不要安装呀,你就没有自带的?我还是只需要配置一下,就可以解析客户端传递 x-www-form-urlencoded 的格式数据,你呢?
koa:哈哈哈,不好意思,我不用配置,我也能解析 x-www-form-urlencoded 的格式数据。
koa:我还能安装 @koa/multer 来实现文件上传,你自带了吗?
express:额。。。我这个还真没自带,我也需要安装 multer 来实现。
koa:让你装 xxx。你我本是同根生,相煎何太急,和平相处不行嘛。
express:。。。
TJ:莫吵了,把你俩创建出来,设计理念本就是不相同的。express 你走的完整路线,koa 走的是灵活路线,所以不用相互较劲,和气生财。
express 和 koa 的核心,都是中间件。
简单的来说,理解了中间件,也就理解了express 和 koa。
那么何为中间件呢?
express 和 koa 都能创建一个服务器实例 app,那么给 app 传入一个回调函数,那么该回调函数就被称为中间件(middleware)。
express 创建中间件:
// 方式一:use(fn)
app.use((req, res, next) => {})
// 方式二:use(path, fn)
app.use('/', (req, res, next) => {})
// 方式三:method(path, fn)
app.get('/', (req, res, next) => {})
// 方式四:method(path, fn1, fn2, fn3)
app.get('/', (req, res, next) => {}, (req, res, next) => {})
koa 创建中间件:
// 方式一:use(fn)
app.use((ctx, next) => {})
// 方式二:use(path, fn)
app.use('/', (ctx, next) => {})
在这里就不展开怎么使用中间件了,我相信你会的,express 和 koa 的中间件道理是相同的。
app.use((req, res, next) => {
console.log("中间件01: next前");
next();
console.log("中间件01: next后");
});
app.use((req, res, next) => {
console.log("中间件02: next前");
next();
console.log("中间件02: next后");
});
app.use((req, res, next) => {
console.log("中间件03")
});

function mockAsync () {
return new Promise((resolve) => {
setTimeout(() => {
resolve(321)
}, 1000)
})
}
app.use((req, res, next) => {
console.log("中间件01: next前");
next();
console.log("中间件01: next后");
});
app.use((req, res, next) => {
console.log("中间件02: next前");
next();
console.log("中间件02: next后");
});
app.use(async (req, res, next) => {
const data = await mockAsync()
console.log("中间件03: next前");
});

app.use((ctx, next) => {
console.log('中间件01: next前');
next()
console.log('中间件01: next后');
})
app.use((ctx, next) => {
console.log("中间件02: next前");
next();
console.log("中间件02: next后");
});
app.use((ctx, next) => {
console.log("中间件03");
});

function mockAsync() {
return new Promise((resolve) => {
setTimeout(() => {
resolve(321);
}, 1000);
});
}
app.use((ctx, next) => {
console.log('中间件01: next前');
next()
console.log('中间件01: next后');
})
app.use((ctx, next) => {
console.log("中间件02: next前");
next();
console.log("中间件02: next后");
});
app.use(async (ctx, next) => {
const res = await mockAsync()
console.log("中间件03");
});

上面四个案例,分别从:
来分析了中间的执行顺序,可以得出两点结论:
first in last out)。所以对于 express 和 koa 在中间件执行上,**表现形式**上是相同的。
而不相同之处就是在于 express 和 koa 在针对中间件存在异步代码时,**处理方式**不同(简单的来说也就是内部的 next 函数实现不同)。
假如存在这样的一个案例:存在两个中间件,一个是业务接口处理的中间件,一个是针对处理相同数据的中间件(比如:针对每个接口返回用户信息),这里的获取用户信息,就是异步操作。
那么针对 express 会写出以下代码:
function getUserInfo () {
return new Promise((resolve) => {
setTimeout(() => {
resolve({name: 'copyer', sex: 'man'})
}, 1000)
})
}
app.get('/list', (req, res, next) => {
const list = [
{ id: "1", content: "列表1" },
{ id: "2", content: "列表2" },
];
next();
res.json({
list,
userInfo: req?.userInfo // 返回用户信息,需要通过下个中间件来获取
})
});
app.use(async (req, res, next) => {
// mock 异步代码,拿到用户信息
const data = await getUserInfo();
console.log(data); // { name: 'copyer', sex: 'man' }
req.userInfo = data; // 设置用户信息
});
当我们访问 list 接口时,发现是拿不到用户信息(userInfo),因为遇到是异步函数,就会停止继续执行下面的代码,而是回到上一个中间件继续执行,所以没有拿到用户信息。
koa 也是同样的道理,但是 koa 却是可以解决这个问题。代码如下:
function getUserInfo() {
return new Promise((resolve) => {
setTimeout(() => {
resolve({ name: "copyer", sex: "man" });
}, 1000);
});
}
router.get('/list', async (ctx, next) => {
const list = [
{ id: "1", content: "列表1" },
{ id: "2", content: "列表2" },
];
await next(); /*****************重点代码*********************/
ctx.body = {
list,
ctx: ctx?.userInfo
}
});
router.use(async (ctx, next) => {
const res = await getUserInfo();
console.log(res); // { name: 'copyer', sex: 'man' }
ctx.userInfo = res; // 设置用户信息
});
app.use(router.routes())
看上面标记的重点代码,那么就是处理异步的关键。在 next 执行前面,加上一个 await,这样就能正常的拿到用户信息。
await next() 的意思就是等待下个中间件执行完成,那么这样用户信息也就设置成功了。
那么肯定有人这样想,那么我在 express 调用 next 函数时,也加上 await ,是不是也解决了?想法很美好,但是行不通的。为撒?
express 中的 next 函数,返回值一个void,没有返回值。
这里的 next 函数接受一个参数 err,也就是错误信息,针对全局异常处理的收集时,就会使用。

koa 中的 next 函数,返回值一个Promise。
这里的 next 函数不接受参数,所以全局错误的异常处理,需要另想它法。

koa 和 express 在异步处理函数,最大的差别就是 next 函数实现不同,那么也就造成了中间件在异步上的使用不同。
koa 在上一个中间件拿取下一个异步中间件的数据,然后返回。express 却是不行,这是 express 设计上的缺陷。
想想平时生活中的洋葱,是不是一层一层的。

中间件从上往下(从外往里),然后从下往上(从里往外)执行,无论是同步还是异步都满足,koa 是符合洋葱模型的。
express 是在同步的时候是满足洋葱模型的,但是异步的时候却是不能满足洋葱模型。
这篇主要从设计架构和中间件两个方面来解释说明 express 和 koa 之间的差异。
比较差异并不是为了证明谁好谁弱,而是为了另一方面来充分认识框架隐藏的知识点,加深自己理解。
若你觉得有误,请指出;若对你有用,请点个赞。
LL库和HAL库简介LL:Low-Layer,底层库HAL:HardwareAbstractionLayer,硬件抽象层库LL库和hal库对比,很精简,这实际上是一个精简的库。LL库的配置选择如下:在STM32CUBEMX中,点击菜单的“ProjectManager”–>“AdvancedSettings”,在下面的界面中选择“AdvancedSettings”,然后在每个模块后面选择使用的库总结:1、如果使用的MCU是小容量的,那么STM32CubeLL将是最佳选择;2、如果结合可移植性和优化,使用STM32CubeHAL并使用特定的优化实现替换一些调用,可保持最大的可移植性。另外HAL和L
对于^、:和"字符的特殊用途,我找不到很好的引用。 最佳答案 它匹配不是:或"的字符block。[...]-字符类-匹配此类中的字符。例如,[abc]将匹配一个字符,a或b或c。[^...]-否定字符类。+-匹配一个或多个另请参阅:CharacterClasses 关于ruby-什么([^:"]+)doinaRubyregularexpression?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/
思科与华为设备OSPF配置命令对比[Huawei]ospf1//启动OSPF进程,进入OSPF视图Cisco(config)#routerospf110[Huawei]ospf1router-id10.1.1.1//启动OSPF进程,进入OSPF视图,手动输入router-idCisco(config-router)#router-id1.1.1.1[Huawei-ospf-1]area0//创建并进入OSPF区域视图(骨干区域)[Huawei-ospf-1-area-0.0.0.0]network10.0.1.00.0.0.255//配置区域所包含的网段[Huawei-GigabitEthe
在输入编码未知的Ruby1.9中,是否有一种公认的方法来处理正则表达式?假设我的输入恰好是UTF-16编码的:x="foobarbaz"y=x.encode('UTF-16LE')re=/(.*)/x.match(re)=>#bar"1:"bar">y.match(re)Encoding::CompatibilityError:incompatibleencodingregexpmatch(US-ASCIIregexpwithUTF-16LEstring)我目前的方法是在内部使用UTF-8并在必要时重新编码(副本)输入:ify.methods.include?(:encode)#Rub
我在Atom中使用Rubylinter,对于某些行,它会发出以下警告:(...)interpretedasgroupedexpression获取此警告的行示例如下:elsifnot(params[:vacancy].nil?orparams[:vacancy]['company_id'].nil?orparams[:vacancy]['company_id']=="0")应该如何改进该行以使警告消失? 最佳答案 警告是(...)interpretedasgroupedexpression它的意思就是它所说的:在Ruby中,圆括号可以
我花了2天时间尝试从SQLServer安装中导出一个包含用户输入数据的大型文本字段的75,000行表。此数据包含每个纯ascii字符、制表符和换行符。我需要导出CSV,其中每个字段都被引用,并且引用列中的引号被正确转义(“”)。到目前为止,这是我尝试过的:-右键单击ManagementStudio中的数据库并导出到Excel:由于字段太长而失败。-将数据从ManagementStudio导出到带有"文本分隔符和逗号分隔符的平面文件-完全无用,不会在字段中转义引号,使文件完全不明确。-来自命令行的BCP-也不支持引用字段。我需要使用FasterCSVruby库导入。它不允许引号定
文章目录Elasticsearch和MongoDB对比关于ElasticsearchElasticsearch应用场景关于MongoDBMongoDB优点mongodb适用场景Elasticsearch和MongoDB对比Elasticsearch和MongoDB开源许可协议参考Elasticsearch和MongoDB对比关于Elasticsearch官网:https://www.elastic.co/cn/elasticsearch/Elasticistheleadingplatformforsearch-poweredsolutions.Weaccelerateresultsthatma
我正在尝试使用passport.js和本地Passport进行基本的用户名/密码身份验证。虽然failureRedirect确实做了它应该做的事情,(重定向到指定页面),successRedirect一直挂起对其指定页面的请求,并在一些之后时间,它返回空响应。http://www.deviantpics.com/VdG正如您在这张图片中看到的,当它请求仪表板时,它说它的大小是0B,但是当我在没有重定向的情况下继续该仪表板时它说它有1.6B。我查看了整个Stackoverflow,但找不到对我有帮助的答案。在我发狂之前,你能检查一下我的代码并提出一些建议吗?这是Passport加载码//
我有一个部署到Heroku实例的Express/Node应用程序。该应用程序有一个POSTapi端点,它需要一个.json文件,读取数据,并使用JSON数据填充应用程序。下面是处理POST请求的后端代码:router.route('/data').post(function(req,res){returnDataUtils.storeData(req,res);});Utils.storeData=function(req,res){req.pipe(req.busboy);req.busboy.on('file',function(fieldname,file,filename){f
我认为自己足以胜任nodeJs。我最近决定通过开始使用Typescript进行开发来改变我的应用程序。我最近看到许多博客(likethisone)在创建RESTfulAPI时,他们将所有模块和应用程序的所有入口点包装在一个类中。它是否正确,或者我可以继续像以前一样使用typescript开发我的应用程序吗? 最佳答案 这是一个风格问题,而不是其他任何问题。但是Express不会为其单元推广OOP,并且将应用程序定义为类没有明显的好处:classApp{publicapp:express.Application;constructor