koa 发布已经快 6 年的时间,做为继 express 以后 node 服务框架最大的黑马,有不少的设计思想值得咱们学习,本文从简到繁逐步介绍 koa,同时适合新老手阅读。html
这里引用中文官方网站的原文前端
Koa 是一个新的 web 框架,由 Express 幕后的原班人马打造, 致力于成为 web 应用和 API 开发领域中的一个更小、更富有表现力、更健壮的基石。 经过利用 async 函数,Koa 帮你丢弃回调函数,并有力地加强错误处理。 Koa 并无捆绑任何中间件, 而是提供了一套优雅的方法,帮助您快速而愉快地编写服务端应用程序。vue
既然是 web 框架你们必定不陌生,经过启动一个 node http server,监听一个端口,进而咱们就能够经过相似 localhost:3000
在本地访问咱们的服务了,这个服务能够是 web 网站,能够是 restful 接口,也能够是静态文件服务等等。node
任何语言、框架都存在 Hello Word
示例,来表达其最简单的入门 Demo,代码以下web
此时访问浏览器 localhost:3000
,咱们会看到打印出了 Hello Word
,此时一个基于 koa 的服务就启动完成了。express
理解 koa 第一步,搞清楚上下文的做用api
例如:微信群里面有人说外面下雪了,你跑到窗边看到的倒是晴空万里,这时你才意识到一样是 10 月份,他在寒冷的北方,你在酷暑的南方。数组
相似的,一次请求会包含用户的登陆状态,或者一些Token之类的信息,这些信息就是上下文的一部分,用于肯定一次的请求环境。浏览器
Koa 的 Context 把 node 的 request, response 对象封装进一个单独对象, 并提供许多开发 web 应用和 APIs 有用的方法. 那些在 HTTP server 开发中使用很是频繁操做, 直接在 Koa 里实现, 而不是放在更高层次的框架, 这样中间件就不须要重复实现这些通用的功能。bash
先来看一个官方的例子:
简单解释下,代码起始初始化一个 koa 实例,下面分别经过 use 方法载入了三个中间件方法,执行顺序:
next()
跳到下一个中间件new Data()
记录当前时间next()
跳到下一个中间件ctx.body
赋值http header
中X-Response-time
打印出来这里的执行顺序延伸出了十分经典的洋葱模型
在一次请求的过程当中会往返通过同一中间件两次,容许咱们处理不一样请求阶段的逻辑。
上面分别介绍了 koa 里面两个最重要的概念,下面咱们分析下 koa 内部是如何运做的,所谓的洋葱模型是如何创建的
koa 源码的 lib 目录十分简单。
lib
|- application.js
|- context.js
|- request.js
|- response.js
复制代码
入口文件是 application.js
,咱们先从这里入手
Application 是一个 class,这个类继承了 node 的 Events 这里不详细展开,在 constructor
中初始化了如下内容:
NODE_ENV
来判断。Object.create
方法将 lib 目录下对应的文件导入到 this
当前上下文,且不污染引入对象。按照正常的编码顺序,在初始化完 koa 实例后(即 const app = new Koa()
),咱们须要调用 app.use()
去挂载咱们的中间件,那么咱们看下 use 方法作了什么
判断中间件为 function,判断中间件是否为 generator function 类型,只是简单的将中间件函数 push
到了 middleware
数组中。
此时心中有没有大写的 WHAT?
其实就是这么直白,没什么复杂逻辑,后面也许你们都猜到了,循环调用 middleware
中的方法去执行,此处还没有代表洋葱模型是怎么来的,咱们先不展开,继续按代码顺序执行。
按照正常的编码顺序,在 use
完咱们的中间件以后就是 app.listen(3000)
一块儿看下这个 listen 干了什么
这里的 http.createServer
就是 node 原生启动 http 服务的方法,这里稍微扩展下基础知识,此方法接受两个参数
options[IncomingMessage, ServerResponse]
这里从 node 版本 v9.6.0, v8.12.0 后才支持,这里不赘述requestListener
此参数为 function 类型,每次请求会传入 req, res
两个参数不难理解这里的 this.callback()
方法必定是返回了一个函数,而且接收两个参数 (req, res)
,下面看下源码
这个 callback 中的信息量有点大,代码自己并不难理解,注释也有说明,从这里展开从上到下分别解释。
这里的 compose 方法主要负责生成洋葱模型,经过 koa-compose
包实现,源码以下
从注释看得出大体逻辑,这里的巧妙之处在于 fn(context, dispatch.bind(null, i + 1))
,
这个 dispatch.bind(null, i + 1)
就是咱们一般写中间件的第二个参数 next
,
咱们执行这个 next()
方法实际上获得的是下一个中间件的执行,
也就不难理解为何咱们 await next()
。 的时候等待的是后面全部中间件串联执行后了,回头再看下上文中间件部分的执行顺序就豁然开朗了。
callback 中的展开解释,看下 const ctx = this.createContext(req, res)
作了什么
这里主要是将 req, res 及 this.request, this.response 都挂载到了 context。 上,并经过赋值理清了循环引用层级关系,为使用者提供方便。
仍是 callback 中的展开解释,看下 this.handleRequest(ctx, fn)
这部分作了什么
分别拿到 ctx 和 compose 生成的洋葱模型,开始逐一消费中间件。
上面理清了总体框架,下面看下 context.js
内部的细节,在文件结尾有两大段的代理。
这里能够看到全部的 req 及 res 的方法集合,那么哪些方法可读,哪些可写,哪些既可读又可写,哪些方法不容许修改
这就是 delegates
这个库作的事情。
delegates
内部利用了,__defineGetter__
和 __defineSetter__
方法控制读写,固然咱们能够从中学习思想,也不能盲从。
这两个 api 去 MDN 上搜索会给出相同的警告信息
This feature is deprecated in favor of defining setters using the object initializer syntax or the Object.defineProperty() API.
其实仍是建议咱们使用 vue 的代理方式 Object.defineProperty()
,不过这个库有四年没更新了依然稳定运行着,仍是深受 koa 开发者承认的。
request.js
和 response.js
文件没什么能够讲,就是具体的工具方法实现,方便开发人员调用,感兴趣能够自行阅读源码。
智联前端架构总体的 node 服务都基于 koa 实现,包括咱们的 vue 服务端渲染和 node restful api 等等。
咱们选择 koa 的缘由是其自己轻巧,可扩展性良好,支持 async、await 的异步,完全摆脱了回调地狱。
市面上也有成熟基于 koa2 的企业级解决方案,如 eggjs 和 thinkjs。
揭开 koa 的神秘面纱,让开发者关注业务逻辑同时也关注下框架自己,有利于问题排查和编写扩展,与此同时能够学习 express、hapi 等同类型框架的思想,结合现有企业级解决方案,选一款适合你的框架,总之框架不论好坏,只论场景。