1. 首页
  2. IT资讯

手把手教你实现Koa2.js

“u003Cdivu003Eu003Ch1u003E介绍u003Cu002Fh1u003Eu003Cpu003E本文能帮你干什么?u003Cu002Fpu003Eu003Colu003Eu003Cliu003EKoa 是一个新的 web 框架,由 Express 幕后的原班人马打造, 致力于成为 web 应用和 API 开发领域中的一个更小、更富有表现力、更健壮的基石。学习一门新的技术,读我们来说很重要。u003Cu002Fliu003Eu003Cliu003EKoa 源码极简,只有短短不到两千行,对于想要了解框架底层原理的同学来说,这很方便。u003Cu002Fliu003Eu003Cliu003E本文中绝大多数代码都是参考Koa2源码,去除了枝干,保留了主干的部分,如果有说得不对的地方,烦请指正。` 下面就一起来看看具体的内容吧!u003Cu002Fliu003Eu003Cu002Folu003Eu003Ch1u003E总览u003Cu002Fh1u003Eu003Cpu003EKoa实际上对nodejs的 http 模块做了简单的封装,下面我将从以下几点来说明:u003Cu002Fpu003Eu003Culu003Eu003Cliu003Estep1. 先来声明一个Koa类,列出核心方法。u003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpreu003Emodule.exports = class Koa {u003Cbru003E constructor() {u003Cbru003E u002Fu002F 这里用于初始化一些实例变量 如 middwareu003Cbru003E }u003Cbru003E u002F**u003Cbru003E * Koa最终重要的方法之一,注册一个中间件u003Cbru003E *u002Fu003Cbru003E use(fn) {}u003Cbru003E u002F**u003Cbru003E * 在实例化Koa对象后,调用listen方法,��监听端口,开启WEB服务u003Cbru003E * 该方法内部会调用�nodejs http模块的createServer方法创建u003Cbru003E * 一个 web server,并传入args参数u003Cbru003E *u002Fu003Cbru003E listen(…args) {}u003Cbru003E};u003Cbru003Eu003Cu002Fpreu003Eu003Culu003Eu003Cliu003Estep2. 实现listen方法,开启服务u003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpreu003Elet http = require(‘http’);u003Cbru003Emodule.exports = class Koa {u003Cbru003E constructor() {u003Cbru003E u002Fu002F 这里用于初始化一些实例变量 如 middwareu003Cbru003E }u003Cbru003E u002F**u003Cbru003E * Koa最终重要的方法之一,注册一个中间件u003Cbru003E *u002Fu003Cbru003E use(fn) {}u003Cbru003E u002F**u003Cbru003E * 在实例化Koa对象后,调用listen方法,监听端口,开启WEB服务u003Cbru003E * 该方法内部会调用nodejs http模块的createServer方法创建u003Cbru003E * 一个 web server,并传入args参数u003Cbru003E *u002Fu003Cbru003E listen(…args) {u003Cbru003E let server = http.createServer((req,res)=>this.callback(req,res));u003Cbru003E server.listen(…args);u003Cbru003E }u003Cbru003E u002F**u003Cbru003E * 在这里处理每一次的请求u003Cbru003E *u002Fu003Cbru003E callback(req,res){u003Cbru003E u002Fu002F 这里要做的下面的几件事情u003Cbru003E u002Fu002F 1. compose middleware: 将中间件函数组合成一个函数 fnu003Cbru003E u002Fu002F 2. 将req和res封装为context对象u003Cbru003E u002Fu002F 3. 处理请求并返回结果u003Cbru003E res.end(‘this is KoaLite!’);u003Cbru003E }u003Cbru003E};u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E现在编写一段代码来测试一下上面的webserver 是否工作u003Cu002Fpu003Eu003Cpreu003Elet koa = require(‘.u002FKoa’)u003Cbru003Elet app = new koa()u003Cbru003Eapp.listen(3000);u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E运行上面的代码,在浏览器中输入u003Cu002Fpu003Eu003Cpreu003Elocalhost:3000u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E可以看到下面的结果:u003Cu002Fpu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002F347a171168c84eea97bdf0d0096bac93″ img_width=”828″ img_height=”182″ alt=”手把手教你实现Koa2.js” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Culu003Eu003Cliu003Estep3. 中间件(middleware)实现u003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003E1.在constructor中添加 middlewares 实例属性,用于保存所有注册的middlewaresu003Cu002Fpu003Eu003Cpreu003E constructor() {u003Cbru003E u002Fu002F 这里用于初始化一些实例变量 如 middwareu003Cbru003E this.middlewares = [];u003Cbru003E }u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E2.实现use方法u003Cu002Fpu003Eu003Cpreu003E u002F**u003Cbru003E * Koa最终重要的方法之一,注册一个中间件u003Cbru003E *u002Fu003Cbru003E use(fn) {u003Cbru003E u002Fu002F 参数fn应该为‘function’,为了不影响阅读,这里参数fn验证跳过u003Cbru003E this.middlewares.push(fn);u003Cbru003E return this;u003Cbru003E }u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E3.实现compose功能 从上面1,2两点可以看出,我们使用 use 方法将所有注册的方法保存在一个 middlewares数组中, 接下来需要实现一个compose function,将所有的middleware函数组成一个函数。新建一个compose.js, 内容如下:u003Cu002Fpu003Eu003Cpreu003Emodule.exports = function(middlewares) {u003Cbru003E u002Fu002F middlewares 是用 app.use(fn) 注册的所有中间件,是一个数组u003Cbru003E return function(context, next) {u003Cbru003E let len = middlewares.len;u003Cbru003E let index = 0;u003Cbru003E if (len === 0) return Promise.resolve(); u002Fu002F 没注册任何中间件的情况u003Cbru003E return dispatch(index);u003Cbru003E u002F**u003Cbru003E * 从第 0 个中间件函数开始执行,并且将下一个中间件函数注入到当前执行的中间件函数中去,u003Cbru003E * 由当前的中间件函数决定是否继续往后执行接下来的函数u003Cbru003E * eg.u003Cbru003E *u003Cbru003E * app.use(async (ctx,next)=>{u003Cbru003E * await next(); u002Fu002F 这里的next是下一个中间件函数,调用它则往后执行接下来的middlewareu003Cbru003E * })u003Cbru003E *u003Cbru003E * @param i 中间件函数下标u003Cbru003E *u002Fu003Cbru003E function dispatch(i) {u003Cbru003E let fn = middlewares[i];u003Cbru003E index += 1;u003Cbru003E if (!fn) return Promise.resolve();u003Cbru003E return Promise.resolve(fn(context, dispatch.bind(null, index)));u003Cbru003E }u003Cbru003E };u003Cbru003E};u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E4.在处理请求时,调用middleware函数,从而实现middleware功能u003Cu002Fpu003Eu003Cpreu003Eu002F**u003Cbru003E * 在这里处理每一次的请求u003Cbru003E *u002Fu003Cbru003E callback(req, res) {u003Cbru003E u002Fu002F 这里要做的下面的几件事情u003Cbru003E u002Fu002F 1. compose middleware: 将中间件函数组合成一个函数 fnu003Cbru003E let middlwareFN = compose(this.middlewares);u003Cbru003E u002Fu002F 2. 将req和res封装为context对象,这里只做简单封装,更详细的后续将提及u003Cbru003E let context = { req, res };u003Cbru003E u002Fu002F 3. 处理请求,在处理请求(request)完后,再处理响应(response)u003Cbru003E this.handleRequest(context, middlwareFN);u003Cbru003E }u003Cbru003E u002F**u003Cbru003E *u003Cbru003E * @param ctx 上下文对象u003Cbru003E * @param middlwareFN 组合后的中间件函数u003Cbru003E *u002Fu003Cbru003E handleRequest(ctx, middlwareFN) {u003Cbru003E middlwareFN(ctx)u003Cbru003E .then(() => this.handleResponse(ctx))u003Cbru003E .catch(this.handleError); u003Cbru003E }u003Cbru003E handleResponse(ctx) {u003Cbru003E let body = ctx.res.body;u003Cbru003E let res = ctx.res;u003Cbru003E u002Fu002F 处理不同类型的 context bodyu003Cbru003E if (Buffer.isBuffer(body)) return res.end(body);u003Cbru003E if (“string” == typeof body) return res.end(body);u003Cbru003E if (body instanceof Stream) return body.pipe(res);u003Cbru003E u002Fu002F body: jsonu003Cbru003E body = JSON.stringify(body);u003Cbru003E ctx.res.end(body);u003Cbru003E }u003Cbru003E handleError() {u003Cbru003E u002Fu002F处理错误略过u003Cbru003E }u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E5 . 测试一下middleware功能u003Cu002Fpu003Eu003Cpreu003Elet koa = require(‘.u002FKoa’)u003Cbru003Elet app = new koa()u003Cbru003Eapp.use(async (ctx,next)=>{u003Cbru003E ctx.res.body= “<p>this is the first middleware<u002Fp>”u003Cbru003E await next();u003Cbru003E})u003Cbru003Eapp.use(async (ctx,next)=>{u003Cbru003E ctx.res.body+= “<p>this is the second middleware<u002Fp>”u003Cbru003E await next();u003Cbru003E})u003Cbru003Eapp.use(async (ctx,next)=>{u003Cbru003E ctx.res.body+= “<p>this is the third middleware<u002Fp>”u003Cbru003E await next();u003Cbru003E})u003Cbru003Eapp.listen(3000);u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E浏览器运行结果如下: u003Cu002Fpu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002Fe274e2f0c2ed4983a92f51243f8e92ee” img_width=”790″ img_height=”310″ alt=”手把手教你实现Koa2.js” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003E 中间件的功能就实现啦!u003Cu002Fpu003Eu003Culu003Eu003Cliu003Esetp4. 将 request 和 response 对象封装到 context 对象u003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003E这一块的内容其实很简单,只需要将req和res中的一些属性或方法交给context对象代理 具体包含以下的一些信息u003Cu002Fpu003Eu003Cpreu003Eu002F**u003Cbru003E * Response delegation.u003Cbru003E *u002Fu003Cbru003Edelegate(proto, ‘response’)u003Cbru003E .method(‘attachment’)u003Cbru003E .method(‘redirect’)u003Cbru003E .method(‘remove’)u003Cbru003E .method(‘vary’)u003Cbru003E .method(‘set’)u003Cbru003E .method(‘append’)u003Cbru003E .method(‘flushHeaders’)u003Cbru003E .access(‘status’)u003Cbru003E .access(‘message’)u003Cbru003E .access(‘body’)u003Cbru003E .access(‘length’)u003Cbru003E .access(‘type’)u003Cbru003E .access(‘lastModified’)u003Cbru003E .access(‘etag’)u003Cbru003E .getter(‘headerSent’)u003Cbru003E .getter(‘writable’);u003Cbru003Eu002F**u003Cbru003E * Request delegation.u003Cbru003E *u002Fu003Cbru003Edelegate(proto, ‘request’)u003Cbru003E .method(‘acceptsLanguages’)u003Cbru003E .method(‘acceptsEncodings’)u003Cbru003E .method(‘acceptsCharsets’)u003Cbru003E .method(‘accepts’)u003Cbru003E .method(‘get’)u003Cbru003E .method(‘is’)u003Cbru003E .access(‘querystring’)u003Cbru003E .access(‘idempotent’)u003Cbru003E .access(‘socket’)u003Cbru003E .access(‘search’)u003Cbru003E .access(‘method’)u003Cbru003E .access(‘query’)u003Cbru003E .access(‘path’)u003Cbru003E .access(‘url’)u003Cbru003E .access(‘accept’)u003Cbru003E .getter(‘origin’)u003Cbru003E .getter(‘href’)u003Cbru003E .getter(‘subdomains’)u003Cbru003E .getter(‘protocol’)u003Cbru003E .getter(‘host’)u003Cbru003E .getter(‘hostname’)u003Cbru003E .getter(‘URL’)u003Cbru003E .getter(‘header’)u003Cbru003E .getter(‘headers’)u003Cbru003E .getter(‘secure’)u003Cbru003E .getter(‘stale’)u003Cbru003E .getter(‘fresh’)u003Cbru003E .getter(‘ips’)u003Cbru003E .getter(‘ip’);u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E并且还增加了一些工具方法,如 inspect , toJSON 等。 详细代码可以参见源码中的context.js u003Cu002Fpu003Eu003Cpu003E本文所有的源码可以再我的github “flyOstrich” dev_blog repository中找到, 希望对大家深入理解Koa有更多的帮助!u003Cu002Fpu003Eu003Cu002Fdivu003E”

原文始发于:手把手教你实现Koa2.js

主题测试文章,只做测试使用。发布者:℅傍ㄖ免沦陷dε鬼,转转请注明出处:http://www.cxybcw.com/11208.html

联系我们

13687733322

在线咨询:点击这里给我发消息

邮件:1877088071@qq.com

工作时间:周一至周五,9:30-18:30,节假日休息

QR code