Koa源码解析

272 阅读2分钟

源码阅读的提前准备

什么时候进行源码阅读 除了一定的编程基础之外,应对该项目具有一定的理解,知道该以什么样的方案解决什么问题。 在github仓库的地址上github后面加上 1s 可以在网页上利用vscode查看源码,支持快捷方式文件跳转,全局搜索等。

例如:https://github1s.com/nodejs/node/blob/HEAD/benchmark/es/defaultparams-bench.js

推荐学习网站:

  1. www.30secondsofcode.org/
  2. github.com/lodash/loda…
  3. 高赞文章之mvvm
  4. 高赞文章之如何搭建开源项目
  5. ...

怎么进行源码阅读

入口

对于js项目,可以直接从package.json的main、bin字段入手入口, 亦可参考files字段。

tip: 对于Monorepo则需要从编译工具入手。

node_modules里面

对应无须编译的项目,可直接在node_modules文件夹里面查看,推荐使用vscode 插件,Search node_modules 来搜索打开项目。

下载github源码查看

对于需要编译的项目,如React、vue组件库或者基于ts等项目,推荐下载github到本地,按照 CONTRIBUTING.md 的指引来开始。

koa是什么,解决了什么问题

koa仅仅只是web框架,真正成熟的企业级的框架可以参考eggjsthinkjs。koa与eggjs的关系,可大致 类比 react与umijs的关系。 本文仅介绍koa的2.x。

大致过一遍

koa入口文件,为lib/application,Koa type github链接

const app = new Koa()

koa关键点:

  • middleware koa洋葱模型
  • context,上下文
  • onerror, 错误处理

洋葱模型

洋葱模型示意图

image.png

洋葱模型中间件示意图

image.png

DOM事件流

image.png

可以将server的request 可类比于 DOM的click事件。可以将 next(); 之前的任意代码视为“捕获”阶段,之后为冒泡。

中间件执行顺序

  1. 创建一个跟踪响应时间的日期
  2. 等待下一个中间件的控制
  3. 创建另一个日期跟踪持续时间
  4. 等待下一个中间件的控制
  5. 将响应主体设置为“Hello World”
  6. 计算持续时间
  7. 输出日志行
  8. 计算响应时间
  9. 设置 X-Response-Time 头字段
  10. 交给 Koa 处理响应

image.png

koa-compose的源码

函数调研堆栈

developer.mozilla.org/zh-CN/docs/…

compose含义

github.com/getify/Func…

redux compose源码

  1. github.com/reduxjs/red…
  2. github.com/reduxjs/red…
// 中间件签名 见applyMiddleware.ts
// 核心之applyMiddleware.ts
funcs.reduce((a, b) => (...args: any) => a(b(...args)))
const middlewareAPI: MiddlewareAPI = { 
     getState: store.getState, 
     dispatch: (action, ...args) => dispatch(action, ...args) 
 } 
 const chain = middlewares.map(middleware => middleware(middlewareAPI)) 

// 核心之compose.ts
 dispatch = compose<typeof dispatch>(...chain)(store.dispatch) 

koa compose源码链接

https://github.com/koajs/compose/blob/master/index.js
// 中间件签名
async (ctx, next) => {}
// 可简单理解为:用户代码也为最深处的中间件
// 核心
Promise.resolve(fn(context, dispatch.bind(null, i + 1)))

上下文

只有一点,将示例作为属性挂在ctx上,没啥特殊的。delegates 包就是属性劫持。 Object.create(proto),创建一个空对象,其__proto__为proto。

  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;
  }

错误处理

ctx.onerror

中间件中的错误捕获, 处理请求 中间件的错误可以被app级别的onerror捕获, this.app.emit('error', err, this); github.com/koajs/koa/b…

默认onerror

github.com/koajs/koa/b… github.com/koajs/koa/b…

自定义onerror

class Application extends Emitter app.on('error', (err) => {})

More

co...