介绍最近在写koa2相关的例子。对了,我看过koa2的源码。以下是一些个人的理解。koa1的核心是基于generator,但是严重依赖co的封装。koa2完全不需要,基于async(其实质是generator的语法糖调用封装),在nodev7下可以直接运行。关于async和generator的语法,本文不再赘述。接下来先创建一个koa实例,然后根据入口一步步分析。//koacodeconstKoa=require('koa');constapp=newKoa();app.use(asyncfunction(ctx,next){console.log('>>one');awaitnext();console.log('<{ctx.body='helloworldgcy';});app.listen('3000',function(){console.log('监听3000端口');});可见上面的代码看似有点神秘,其实质是对下面http模块的封装调用。//本机小码http=require('http');letserver=http.createServer(function(req,res){res.writeHead(200,{'Content-type':'text/plain'});res.write('helloworldgcy');res.end();});//启动服务listenserver.listen(8000,function(){console.log('监听8000端口');});下面根据koa的构造函数入口做进一步分析。构造函数(){超级();this.proxy=false;//创建一个空数组存放中间件,洋葱过程的真相,下面会分析到this.middleware=[]的方法;//确定要忽略的子域数量,默认为2this.subdomainOffset=2;//进程环境变量this.env=process.env.NODE_ENV||'发展';//在实例上挂载上下文、请求、响应this.context=Object.create(context);this.request=Object.create(请求);this.response=Object.create(response);}说明上面是构造函数入口,启动入口如下//借用原来的http.createServer,加上app.callback。listen(){debug('listen');constserver=http.createServer(this.callback());返回server.listen.apply(服务器,参数);}说明一个完整的web服务器通过以上两步就建立起来了。监听器收到的请求的解析处理是通过回调函数调用一系列中间件完成的。下面分析一下中间件的执行过程。我觉得koa的主要内涵就在这里,下面重点说一下。先介绍一下经典的中间件洋葱图,便于理解。结合这张图看下面的代码////核心代码应用126行constfn=compose(this.middleware);returnfunction(context,next){letindex=-1returndispatch(0)functiondispatch(i){if(i<=index)returnPromise.reject(newError('next()被多次调用'))索引=iletfn=middleware[i]if(i===middleware.length)fn=next//立即返回resolve状态的promise实例,以便后续逻辑继续执行if(!fn)returnPromise.resolve()try{//awaitnext();//当这句话在fn中执行时,会执行dispatch(i+1),导致onion的执行过程//整个过程类似于栈执行释放过程中的递归调用,虽然有区别,你可以类比一下其执行时序流程returnPromise.resolve(fn(context,functionnext(){returndispatch(i+1)}))}catch(err){returnPromise.reject(err)}}//核心代码应用第136行returnfn(ctx)[isapromiseinimmediatestate].then(handleResponse).catch(onerror);}}如果在结合注释阅读上述代码的过程中有疑惑,可以进一步参考下文进行思考,否则直接忽略即可。constKoa=require('koa');constapp=newKoa();app.use(asyncfunction(ctx,next){console.log('>>one');awaitnext();console.log('<>two');ctx.body='two';awaitnext();console.log('<>three');awaitnext();console.log('<{ctx.body='helloworldgcy';});app.listen('3000',function(){console.log('监听3000端口');});说明koa是基于中间价格结构的,核心很简单。除此之外,还有一些其他比较重要的方法。application.jsuse(fn)//程序集使用参数通过createContext(req,res)创建一个初始化的context,在context上挂载req和resonerror(err)错误处理,当this.slient设置为true时,no会输出information,触发emit时执行respond(ctx),简单封装httpresponse,返回信息给context.jsdelegate(proto,'request')//请求相关的方法委托,这样context就可以作为onerror的调用入口(err)//intermediate除了程序执行过程中的异常处理逻辑外,还有请求和响应参数分析文件,因为逻辑简单,就不做说明了。虽然核心文件不多,但也需要很多包。这里举几个重的作为例子。requireevents应用继承自Emitter,从而实现事件的订阅和发布。上面已经分析了核心逻辑之一的koa-compose中间件的封装。debug报错信息格式封装流程状态http状态码及对应信息处理koa-convert转generator转promise...等等总结写这篇文章的原因是在写案例的过程中,同解决中间件下的纠结选择,尤其是在选择渲染模板的过程中,就是要找到它们之间的本质区别。源码分析基于koa(2.2.0版本)。通读源码后,可以更清楚的开发或使用别人的中间件。如果您有不同的理解,欢迎留言交流。