Koa 解析( 二 )
错误处理逻辑在 Koa 内部 , 由于是洋葱圈模型 , 正常的中间件处理过程可以不处理报错逻辑 , 只需要在中间件数组的第一个中间件设置为错误函数中间件即可 。 而在 Express 4 中 , 需要在每一个正常的中间件中都包含错误处理逻辑 , 并且需要通过 next() 函数抛出来 , 否则该错误将会被隐没 。
更小的核心代码与 Express 4 包含有齐整的 Web 工具(路由、模板等)不同 , Koa 只专注于核心代码 。 因此它的体积更小 。
Koa 解析简单的描述并不能让我们对 Koa 的原理有更深的理解 。 我们尝试来看看源码 。 最简单的演示代码:
const Koa = require('koa');const app = new Koa();// responseapp.use(ctx => {ctx.body = 'Hello Koa';});console.log("start the test1 server !");app.listen(3000);
在这里 , 我们想要去源码上弄明白几个事情:
app.use()app.listen()
Koa 初始化应用实例Koa 应用中 , 我们通过 const app = new Koa() 来构建一个实例应用 。 核心代码(有删减):
constructor(options) {super();this.middleware = [];// 每一个 app 实例 , 都有下面三个对象的实例this.context = Object.create(context);this.request = Object.create(request);this.response = Object.create(response);if (util.inspect.custom) {this[util.inspect.custom] = this.inspect;}}
初始化应用实例的过程可以看出:为 app 实例添加 context 、 request 、 response 、 middleware 等属性 。在这个初始化的过程中 , Koa 会把 Koa 官方提供的方法都挂载到相应的位置 , 方便在代码中调用 。
app.use() 添加中间件use(fn) {if (typeof fn !== 'function') throw new TypeError('middleware must be a function!');if (isGeneratorFunction(fn)) {deprecate('Support for generators will be removed in v3. ' +'See the documentation for examples of how to convert old middleware ' +'');fn = convert(fn);}debug('use %s', fn._name || fn.name || '-');// 直接存入到 middleware 数组中 , 后续统一处理this.middleware.push(fn);return this;}
在这里会检查传入函数的类型 , 如果是老的 Generator 函数类型会转换一下 , 然后直接放到 middleware 这个数组中 。 数组中的中间件 , 会在每一个请求中去挨个执行一遍 。
app.listen() 监听--核心逻辑在 listen 函数执行的时候 , 才会创建 server :
listen(...args) {debug('listen');const server = http.createServer(this.callback());return server.listen(...args);}
在 Node 的 http 模块 , 对于每一个请求 , 都会走到回调函数 callback 中去 。 所以这个 callback 是用于处理实际请求的 。 我们来看看 callback 做了啥:
callback() {// 包装所有的中间件 , 返回一个可执行的函数 。 koa-compose 实现了洋葱圈模型const fn = compose(this.middleware);if (!this.listenerCount('error')) this.on('error', this.onerror);const handleRequest = (req, res) => {// req res 是 node 原生请求参数const ctx = this.createContext(req, res);// 将创建的 ctx 返回 , 传给所有中间件 , 作为整个请求的上下文return this.handleRequest(ctx, fn);};return handleRequest;}
这里的内容并不简单 , 涉及到几个点:
createContextcomposethis.handleRequest(ctx, fn)
解析 createContextcreateContext(req, res) {// 每一个请求对应一个 ctx、request、response、req、resconst 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;// 挂载 node 原生请求参数 req res 到 context、request、response 上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;}
- 大牛深入解析SpringBoot核心运行原理和运作原理源码
- 一线大牛带你深入解析AutoConfiguration源码
- Python爬虫入门第一课:如何解析网页
- Scala中可变和不可变之图文解析
- 「自动驾驶·可靠性」问题解析:功能描述需谨慎
- 解决:海尔电视哔哩哔哩TV版下载后安装不了,显示解析包错误?
- 来自四川泸州!全国首个工业互联网标识解析白酒行业节点上线
- Fender美豪、美精、美超到底哪个更好?全面解析!
- 全国首个工业互联网标识解析白酒行业节点上线
- 隆腾君|隆腾智能解析数控机床智能原理