You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
var EventEmitter = require('events').EventEmitter;
var proto = require('./application');
var req = require('./request');
var res = require('./response');
exports = module.exports = createApplication;
function createApplication() {
// 定义 app 字面量函数
var app = function(req, res, next) {
app.handle(req, res, next);
};
// defines EventEmitter.prototype's descriptors on app if app does not have a descriptor by the same name
mixin(app, EventEmitter.prototype, false);
mixin(app, proto, false);
// expose the prototype that will get set on requests
app.request = Object.create(req, {
app: { configurable: true, enumerable: true, writable: true, value: app }
})
// expose the prototype that will get set on responses
app.response = Object.create(res, {
app: { configurable: true, enumerable: true, writable: true, value: app }
})
console.log(app.response.app.response === app.response);
app.init();
return app;
}
简介
Koa是由Express的原班人马打造的新一代web框架,针对web应用和APIs,旨在成为一个更小,更生动,更强壮的基础框架。借助生成器,Koa可以让你不再受回调的困扰,还大幅度提高了错误处理能力。同时,核心不会集成任何中间件,提供了一套更优雅的方法,使我们写服务端代码更快速更加享受。
Koa2 Vs Express
设计哲学上的不同
1、Koa: 为了解决并替换node,Express:增强了node
2、Koa使用promise和async来避免回调地狱,使错误处理简单化。暴露它本身的
ctx.request
和ctx.response
对象,而非node的req
和res
对象。 Express增强了node的req
和res
对象,添加额外的属性和方法,一级包含了一些其他的"框架"特征,比如路由和模板。但Koa没有这样做。3、Koa可以被视为是node
http
模块的抽象,但Express是一个针对node的完整的应用框架。Express原理浅析
我们先只用node的http模块启动一个服务,监听3000端口,以及几个路由:
以上代码的意思是我们启动了一个3000端口的服务,并注册了回调函数,回调函数接收req, res, next三个返回值。当在浏览器输入localhost:3000后,回调函数才会执行。
用express之后,createServer中入参回调函数就换成了下面一个express的app实例:
app使用每一层中间件来处理请求,可以看到通过app.use的方式处理的是应用级别的,比如开始的一些模板cookie等的设置,应用的访问结构(一级访问路径),最后的容错处理等。每一层中间件可以是路由级别的中间件,也可以是个过滤器,拦截器等等。每个请求都会从上到下走一遍,有的中间件是检查权限,有的根据它再获取一些新东西再加到它身上,可能有的让它next(),继续去下一个中间件,有的一拍即合就直接res.render或者res.json,那么这个请求就完成了它的使命,不会再继续往下走了。
按照使用方法,我们逐条对着源码看看express做了什么。
首先是
var app = express()
这句声明。主入口express.js
var app = express()
这句调用得到的app就是createApplication
方法中返回的app。app是个函数,并将node的req, res, next传给了app内部handle方法,赋予handle对于内部对象的控制权。注意这里只是注册handle处理函数,并没有执行。关于以上有一个疑问:
app.response
和app.request
的原型上要暴露app?而app.response.app.response === app.response 执行结果也是true。可以看到,这里app对象里面也暴露了node events模块的方法,除此之外,在这个主文件中还暴露了封装的Route,Router,还有以下几个中间件:
然后主入口里面还有这句函数调用:
app.init()
, 在init中设置了一系列私有变量以及配置项。毕竟在正式开始前,都需要配置一些默认参数。 至此,主入口文件大致搞清楚了。接下来我们看看启动服务监听端口的声明:
app.listen(3000)
这里其实就是执行了
http.createServer(app).listen(3000)
。app.use
重点来了,源码中有大部分是对参数的处理,从最上面的使用代码也可以看到app.use的参数形式多样,总得需要一些判断校验处理,所以省略这些,贴出源码中真正处理的部分:
以
app.use('/', (req, res) => {res.send('hello world')})
调用为例,最终就是执行:router.use('/', (req, res) => {res.send('hello world')})
router.use
同样省去那些校验判断取核心:
调用router.use就会new一个Layer,这个Layer就是一个类,声明如下:
例子带入之后就是Layer.handle = pageRouter; 这样每一次app.use都会通过这样创建一个层,可以看做是 express 在启动运行的时候,注册好了一个中间件函数栈stack,里面堆叠好了待被调用的中间层,一旦请求进来,就会执行回调函数,即正式调用app, 也就是
app()
, app.handle开始执行:app.handle
app.handle又把node服务回调里的三个参数,继续分派给了 express 的核心路由模块。
router.handle
按照helloworld的例子,以上最终走向layer.handle_request(req, res, next)。
layer.handle_request
似乎过程通了,当事件触发时,在express启动时注册的回调函数开始一层一层过滤执行。有点不太清楚为什么要这样通过app.handle => router.handle => layer.handle注册回调函数,或许express目的就是做一个完整的应用框架,需要更多的扩展,需要应用级别的层次感,更多的属性以应对各种各样的web应用请求。毕竟大牛们的脑回路是普通人没法比的。如果没有express,我们的node服务估计就是一大串的
switch-case
或者if-else
,无法想象了!Koa2
先看一个例子:
和Express一样,new一个Koa的实例app,中间件也是通过app.use传递,也是通过app.listen(3000)启动服务监听3000端口,但和Express不同的是,app.listen的实现是这样:
http.createServer(app.callback()).listen(3000)
Koa传入的是实例的callback方法,而express传入的是app本身。
app.use
Koa的use很简单,就是把进来的函数压入到一个数组中。再看app.callback()做了什么:
app.callback
首先是compose方法,简单说来就是把那些通过app.use设置的中间件组装成一个递归函数,递归的是Promise方法,而在中间件数组中都是async函数,async本身就是返回Promise,用递归的方式实现Promise的链式执行。
然后,执行callback(),callback返回了一个local作用域方法handleRequest。这个方法里面通过类方法
createContext
将req, res合成了ctx,我们会用到的全部属性都在ctx里面,然后将ctx和递归函数fn丢给内部方法this.handleRequest
。当我们访问服务时,local的handleRequest(req, res)就触发执行了,并且返回的是this.handleRequest
执行后的返回:这时候compose后的链式Promise函数开始执行。到这里,反正我脑子已经不够用了。很抽象,每一行源码都涉及到很深的基础知识,还有对新特性的深入理解。基础不扎实,而且设计又很抽象,确实看起来比较费力。最后放上两张参考文章-Koa2 还有多久取代 Express里的两张图:
中间件执行顺序:
请求处理洋葱圈模型:
后面用起来慢慢体会吧!看Koa2的源码,最大的感受就是Koa2充分运用新的语言特性,自身代码更简洁。整个框架就是4个js文件,结合注释都不到1K行。正如参考文章作者说的,这都是历史的必然。
对于为什么服务会这样运行,比如,createServer中我们注册的回调函数为什么会在访问浏览器时才触发?这里面需要结合node的事件机制来理解了。我了解不多,参考文章-Koa2 还有多久取代 Express也有提到,很多还不太理解。到能理解的时候,再做些记录。
TODO:
1、搭建Koa2工程化服务
2、理解NodeJS事件机制
The text was updated successfully, but these errors were encountered: