范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

从浅入深了解Koa2源码

  在前文我们介绍过什么是 Koa2 的基础 简单回顾下什么是 koa2NodeJS 的 web 开发框架 Koa 可被视为 nodejs 的 HTTP 模块的抽象 源码重点
  中间件机制
  洋葱模型
  compose 源码结构
  Koa2 的源码地址:https://github.com/koajs/koa
  其中 lib 为其源码
  koa2源码
  可以看出,只有四个文件: application.js  、context.js  、request.js  、response.js  application
  为入口文件,它继承了 Emitter 模块,Emitter 模块是 NodeJS 原生的模块,简单来说,Emitter 模块能实现事件监听和事件触发能力
  application1
  删掉注释,从整理看  Application   构造函数
  Application构造函数
  Application 在其原型上提供了 listen、toJSON、inspect、use、callback、handleRequest、createContext、onerror 等八个方法,其中 listen:提供 HTTP 服务 use:中间件挂载 callback:获取 http server 所需要的 callback 函数 handleRequest:处理请求体 createContext:构造 ctx,合并 node 的 req、res,构造 Koa 的 参数——ctx onerror:错误处理
  其他的先不要在意,我们再来看看 构造器  constructor
  Application的构造器
  晕,这都啥和啥,我们启动一个最简单的服务,看看实例 const Koa = require("Koa")  const app = new Koa()  app.use((ctx) => {   ctx.body = "hello world" })  app.listen(3000, () => {   console.log("3000请求成功") })  console.dir(app)
  实例
  能看出来,我们的实例和构造器一一对应,
  打断点看原型
  断点
  哦了,除去非关键字段,我们只关注重点
  Koa 的构造器上的 this.middleware、 this.context、 this.request、this.response
  原型上有:listen、use、callback、handleRequest、createContext、onerror
  注:以下代码都是删除异常和非关键代码  先看 listen...   listen(...args) {     const server = http.createServer(this.callback())     return server.listen(...args)   } ...
  可以看出 listen 就是用 http 模块封装了一个 http 服务,重点是传入的  this.callback()  。好,我们现在就去看 callback 方法callback  callback() {     const fn = compose(this.middleware)     const handleRequest = (req, res) => {       const ctx = this.createContext(req, res)       return this.handleRequest(ctx, fn)     }     return handleRequest   }
  它包含了中间件的合并,上下文的处理,以及 res 的特殊处理 中间件的合并
  使用了  koa-compose   来合并中间件,这也是洋葱模型的关键,koa-compose 的源码地址:https://github.com/koajs/compose。这代码已经三年没动了,稳的一逼function compose(middleware) {   return function (context, next) {     let index = -1     return dispatch(0)     function dispatch(i) {       if (i <= index)         return Promise.reject(new Error("next() called multiple times"))       index = i       let fn = middleware[i]       if (i === middleware.length) fn = next       if (!fn) return Promise.resolve()       try {         return Promise.resolve(fn(context, dispatch.bind(null, i + 1)))       } catch (err) {         return Promise.reject(err)       }     }   } }
  一晃眼是看不明白的,我们需要先明白 middleware 是什么,即中间件数组,那它是怎么来的呢,构造器中有 this.middleware,谁使用到了—— use 方法
  我们先跳出去先看 use 方法 useuse(fn) {     this.middleware.push(fn)     return this }
  除去异常处理,关键是这两步, this.middleware   是一个数组,第一步往 this.middleware   中 push 中间件;第二步返回 this 让其可以链式调用,当初本人被面试如何做 promise 的链式调用,懵逼脸,没想到在这里看到了
  回过头来看 koa-compose 源码,设想一下这种场景 ... app.use(async (ctx, next) => {     console.log(1);     await next();     console.log(6); }); app.use(async (ctx, next) => {     console.log(2);     await next();     console.log(5); });  app.use(async (ctx, next) => {     console.log(3);     ctx.body = "hello world";     console.log(4); }); ...
  我们知道 它的运行是 123456
  它的 this.middleware 的构成是 this.middleware = [   async (ctx, next) => {     console.log(1)     await next()     console.log(6)   },   async (ctx, next) => {     console.log(2)     await next()     console.log(5)   },   async (ctx, next) => {     console.log(3)     ctx.body = "hello world"     console.log(4)   }, ]
  不要感到奇怪,函数也是对象之一,是对象就可以传值
  const fn = compose(this.middleware)
  我们将其 JavaScript 化,其他不用改,只需要把最后一个函数改成 async (ctx, next) => {   console.log(3);   -ctx.body = "hello world";   +console.log("hello world");   console.log(4); }
  测试compose
  测试compose2 逐行解析 koa-compose
  这一段很重要,面试的时候常考,让你手写一个 compose ,淦它 //1. async (ctx, next) => { console.log(1); await next(); console.log(6); } 中间件 //2. const fn = compose(this.middleware) 合并中间件 //3. fn() 执行中间件  function compose(middleware) {     return function (context, next) {         let index = -1;         return dispatch(0);         function dispatch(i) {             if (i <= index)                 return Promise.reject(                     new Error("next() called multiple times"),                 );             index = i;             let fn = middleware[i];             if (i === middleware.length) fn = next;             if (!fn) return Promise.resolve();             try {                 return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));             } catch (err) {                 return Promise.reject(err);             }         }     }; }
  执行  const fn = compose(this.middleware)  ,即如下代码const fn = function (context, next) {     let index = -1     return dispatch(0)     function dispatch (i) {       if (i <= index) return Promise.reject(new Error("next() called multiple times"))       index = i       let fn = middleware[i]       if (i === middleware.length) fn = next       if (!fn) return Promise.resolve()       try {         return Promise.resolve(fn(context, dispatch.bind(null, i + 1)))       } catch (err) {         return Promise.reject(err)       }     }   } }
  执行  fn()  ,即如下代码:const fn = function (context, next) {     let index = -1     return dispatch(0)     function dispatch (i) {       if (i <= index) return Promise.reject(new Error("next() called multiple times"))       index = i // index = 0       let fn = middleware[i] // fn 为第一个中间件       if (i === middleware.length) fn = next // 当弄到最后一个中间件时,最后一个中间件赋值为 fn       if (!fn) return Promise.resolve()       try {         return Promise.resolve(fn(context, dispatch.bind(null, i + 1)))           // 返回一个 Promise 实例,执行 递归执行 dispatch(1)       } catch (err) {         return Promise.reject(err)       }     }   } }
  也就是第一个中间件,要先等第二个中间件执行完才返回,第二个要等第三个执行完才返回,直到中间件执行执行完毕
  Promise.resolve   就是个 Promise 实例,之所以使用 Promise.resolve   是为了解决异步,之所以使用 Promise.resolve   是为了解决异步
  抛去  Promise.resolve  ,我们先看一下递归的使用,执行以下代码const fn = function () {     return dispatch(0);     function dispatch(i) {         if (i > 3) return;         i++;         console.log(i);         return dispatch(i++);     } }; fn(); // 1,2,3,4
  回过头来再看一次 compose,代码类似于 // 假设 this.middleware = [fn1, fn2, fn3] function fn(context, next) {     if (i === middleware.length) fn = next // fn3 没有 next     if (!fn) return Promise.resolve() // 因为 fn 为空,执行这一行     function dispatch (0) {         return Promise.resolve(fn(context, function dispatch(1) {             return Promise.resolve(fn(context, function dispatch(2) {                 return Promise.resolve()             }))         }))     }   } }
  这种递归的方式类似执行栈,先进先出
  执行栈
  这里要多思考一下,递归的使用,对 Promise.resolve 不用太在意 上下文的处理
  上下文的处理即调用了 createContext createContext(req, res) {     const context = Object.create(this.context)     const request = (context.request = Object.create(this.request))     const response = (context.response = Object.create(this.response))     context.app = request.app = response.app = this     context.req = request.req = response.req = req     context.res = request.res = response.res = res     request.ctx = response.ctx = context     request.response = response     response.request = request     context.originalUrl = request.originalUrl = req.url     context.state = {}     return context }
  传入原生的 request 和 response,返回一个 上下文——context,代码很清晰,不解释 res 的特殊处理
  callback 中是先执行 this.createContext,拿到上下文后,再去执行 handleRequest,先看代码: handleRequest(ctx, fnMiddleware) {     const res = ctx.res     res.statusCode = 404     const onerror = (err) => ctx.onerror(err)     const handleResponse = () => respond(ctx)     onFinished(res, onerror)     return fnMiddleware(ctx).then(handleResponse).catch(onerror) }
  一切都清晰了 const Koa = require("Koa"); const app = new Koa();  console.log("app", app); app.use((ctx, next) => {     ctx.body = "hello world"; }); app.listen(3000, () => {     console.log("3000请求成功"); });
  这样一段代码,实例化后,获得了 this.middleware、this.context、this.request、this.response 四大将,你使用 app.use() 时,将其中的函数推到 this.middleware。再使用 app.listen() 时,相当于起了一个 HTTP 服务,它合并了中间件,获取了上下文,并对 res 进行了特殊处理 错误处理onerror(err) {     if (!(err instanceof Error))         throw new TypeError(util.format("non-error thrown: %j", err))      if (404 == err.status || err.expose) return     if (this.silent) return      const msg = err.stack || err.toString()     console.error()     console.error(msg.replace(/^/gm, "  "))     console.error() } context.js
  引入我眼帘的是两个东西 // 1. const proto = module.exports = {  inspect(){...},     toJSON(){...},     ... } // 2. delegate(proto, "response")   .method("attachment")   .access("status")   ...
  第一个可以理解为,const proto = { inspect() {...} ...},并且 module.exports 导出这个对象
  第二个可以这么看,delegate 就是代理,这是为了方便开发者而设计的 // 将内部对象 response 的属性,委托至暴露在外的 proto 上 delegate(proto, "response")   .method("redirect")   .method("vary")   .access("status")   .access("body")   .getter("headerSent")   .getter("writable");   ...
  而使用  delegate(proto, "response").access("status")...  ,就是在 context.js 导出的文件,把 proto.response 上的各个参数都代理到 proto 上,那 proto.response 是什么?就是 context.response,context.response 哪来的?
  回顾一下, 在 createContext 中 createContext(req, res) {     const context = Object.create(this.context)     const request = (context.request = Object.create(this.request))     const response = (context.response = Object.create(this.response))     ... }
  context.response 有了,就明了了, context.response = this.response,因为 delegate,所以 context.response 上的参数代理到了 context 上了,举个例子 ctx.header 是 ctx.request.header 上代理的 ctx.body 是 ctx.response.body 上代理的 request.js 和 response.js
  一个处理请求对象,一个处理返回对象,基本上是对原生 req、res 的简化处理,大量使用了 ES6 中的 get 和 post 语法
  大概就是这样,了解了这么多,怎么手写一个 Koa2 呢,请看下一篇——手写 Koa2 参考资料KOA2 框架原理解析和实现 可能是目前最全的 koa 源码解析指南

iPhone13小米12和荣耀Magic4,哪款手机更值得买?更建议选择iPhone13,最大的优势就在于性能上搭载的苹果A15芯片不仅性能上领先安卓一个身位,在能耗控制上也比全新骁龙8高两个档次,在加上iOS系统的优化,iPhone13不仅年度折叠预测三星依然是冠军,荣耀令人刮目相看无论折叠屏是否是未来智能手机的终极形态,反正现在除了苹果之外安卓阵营所有品牌都在杀入这个领域。除了极少数的小品牌还没有进入之外,顶部的安卓品牌已经全部进入。甚至有品牌已经商用好几款特斯拉车顶维权当事人发声特斯拉女车主车顶维权案当事人张女士在其个人微博发布视频发布与特斯拉方面民事诉讼案件的相关进展。视频中,张女士表示,这个视频的内容揭露了名誉权案件的事实真相,并指出特斯拉全球副总裁陶有排面!国产机发布会登上美国华尔街日报,高管直言有点傻小米公司成立至今整整12年,为了庆祝这个特殊的日子,雷军晒出最难忘的几张照片,其中一张还登上美国华尔街日报。不少网友评论,果然雷老板有排面,一场发布会都惊动大洋彼岸的报纸,同时也怀百度因擅自转播春晚被央视起诉,判赔超50万Tech星球4月6日消息,近日,央视国际网络有限公司与北京百度网讯科技有限公司著作权权属侵权纠纷民事一审判决书公开。文书显示,原告央视公司诉称,百度公司未经授权许可擅自通过好看视频Java中的多态在Java中实现多态有3个必要条件1,满足继承关系2,要有重写3,父类引用指向子类对象,编译看左边,运行看右边对象的多态性只适用于方法,不适用于属性,属性的编译和运行都看左边多态是OPPOvivo小米宣布新举措不再允许32位应用单独上架IT之家4月6日消息,据移动智能终端生态联盟消息,为了进一步推进国内Android应用生态过渡至64位架构,提升用户体验,该联盟成员OPPOvivo小米共同宣布,自2022年4月起中国真正的高端科技一览!太牛了AI在今天已经是一个非常热门的话题了,但是很少有人从社会学的角度关注它人们要么神圣化它的力量,要么为它给我们生活带来的便利欢呼但具有讽刺意味的是,很多人即便被AI夺走了工作,也依然人性论之宇宙本原(2)大爆炸产生物质与反物质,物质由于引力作用形成星球或星系。在这种理论基础上将有很多问题需要解决星球的死亡时间星系结构空间质量力奇点。空间是什么?在西方科学定义中,空间是三维度的标量,大气科学可助推太阳能工程发展专家顾问哈尔滨工业大学教授杨大智中国科学院大气物理研究所研究员夏祥鳌中国气象局风能太阳能中心科学主任气象服务首席申彦波哈尔滨工业大学博士研究生王文婷在双碳目标背景下,加快清洁能源开技术分享MySQL命令行一则诡异问题分享作者杨涛涛资深数据库专家,专研MySQL十余年。擅长MySQLPostgreSQLMongoDB等开源数据库相关的备份恢复SQL调优监控运维高可用架构设计等。目前任职于爱可生,为各
你知道么?在隐秘的角落宣发海报里竟然有这些惊天大秘密隐秘的角落,一部烧脑悬疑的网剧,最近火遍全网!就连海报都是悬疑剧!隐秘的角落改编自紫金陈的小说坏小孩,由韩三平监制,由秦昊主演是一部国产悬疑推理网剧。这部剧讲述的是三个小孩在景区游聚划算128GiPhone12降至4799看来新款iPhone不远了苹果iPhone13将于9月发布的传闻甚嚣尘上,也引发了各大平台iPhone12的价格跳水。面对新款手机的诱惑,不少坚决不当韭菜的等等党还是更喜欢降价真香的iPhone12。与官网用笨办法学Python这本书适合完全没学过编程,但是对编程感兴趣的人学习使用。通过52个习题,引导初学者一步一步从最简单的打印开始,逐步完成一整个项目。这本书最重要的功能就是引导你进行大量的代码练习,如啊哈!算法一本能看懂的算法书算法在一般人眼中都是很难而且枯燥的形象,这本啊哈!算法用通俗易懂的语言,配合有趣的插图让你在阅读本书的时候更像是在品读一篇篇轻松的短篇小说或是在玩一把题味解谜游戏,在轻松愉悦中掌握拟募资超60亿元,科创板IPO成功过会,AI四小龙之旷视奇才科技明说阿明观察9月9日,上交所披露科创板上市委2021年第66次审议会议结果旷视科技首发过会。据旷视科技9月2日更新的招股书(上会稿)显示,旷视科技此次预计募集发行2。53亿份C伟仕佳杰2021中期业绩最新公布纯利营收双双高增长8月26日,亚太区领先的科技产品渠道开发与技术方案集成服务商伟仕佳杰控股有限公司宣布公司截至2021年6月30日止6个月的中期业绩。后疫情时代,数字经济成为全球科技发展新引擎,伟仕业务中台V3。1发布,打造全员可使用的中台近日,驱动智能商业的中台服务商云徙科技宣布产品升级迭代,正式对外发布业务中台V3。1版本。这是云徙科技业务中台产品在2020年做出的首次重要更新。此次发布的业务中台V3。1版本,重云徙数舰数字中台3。5。2版本重磅升级7月2日,数字中台领导者云徙科技再次对外宣布,面向大型企业的数舰数字中台产品矩阵所囊括的七大产品全部更新到V3。5。2版本。云徙数舰数字中台3。5产品矩阵正式对外亮相之后的第二次迭云徙数盈总经理李元佳丨数有据盈有道数盈的现在与未来演讲人李元佳编辑小徙很多朋友合作伙伴和媒体最近问我数盈是做什么的定位是什么有什么产品和服务。这个要从2019年的营销创新探索讲起。在过去四年,云徙科技的中台服务了很多个行业的头部客一文解读燃爆LG新款超大屏可折叠屏游戏手机的哈氪游侠折叠后整体效果展开后定制化键盘全屏游戏模式周末时光,游戏是我忙碌工作之余的最佳解压方式,手机玩吃鸡让我得到短暂的放松和兴奋。突然看到LG手机发布8英寸,120Hz刷新率的可折叠手机一文解读翻盖手机2021是复古还是新时尚?MOTOV8Hello,MOTO熟悉的声音,许久没有用大拇指弹起前盖的Feeling。在记忆里,摩托罗拉V8是我最喜欢的翻盖手机之一。随着大屏时代的到来,直屏和曲屏占据所有手机的面