二元一次方程组,精油按摩,曾舜晞-雷竞技安卓_雷竞技安卓版_雷竞技官方app下载安卓版

频道:国际新闻 日期: 浏览:212

Koa 2.x 版别是当下最盛行的 NodeJS 结构,Koa 2.0 的源码特别精简,不像 Express 封装的功用那么多,所以大部分的功用都是由 Koa 开发团队(同 Express 是一家出品)和社区贡献者针对 Koa 对 NodeJS 的封装特性完结的中心件来供给的,用法十分简略,便是引进中心件,并调用 Koa 的 use 办法运用在对应的方位,这样就能够经过在内部操作 ctx 完结一些功用,咱们接下来就评论常用中心件的完结原理以及咱们应该怎么开发一个 Koa 中心件供自己和他人运用。

Koa 的洋葱模型介绍

咱们本次不对洋葱模型的完结原理进行过多的刨析,首要依据 API 的运用办法及洋葱模型剖析中心件是怎么作业的。

洋葱模型特色


// 引进 Koa
const Koa = require('koa');
// 创立服务
const app = new Koa();
app.use(async (ctx, next) => {
console.log(1);
await next();
console.log(2);
});
app.use(async (ctx, 情迷阴阳界next) => {
console.log(3);
await next();
console.log朱佑基(4);
});
app.use(async (ctx, next) => {
console.log(5);
await next();
console.log(6);
});
// 监听服务
app.listen(3000);

咱们知道 Koa 的 use 办法是支撑异步的,所以为了确保正常的依照洋葱模型的履行次序履行代码,需求在调用 next 的时分让代码等候,等候异步结束后再持续向下履行,所以咱们在 Koa 中都是主张运用 async/await 的,引进的中心件都是在 use 办法中调用,由此咱们能够剖分出每一个 Koa 的中心件都是回来一个二元一次方程组,精油按摩,曾舜晞-雷竞技安卓_雷竞技安卓版_雷竞技官方app下载安卓版 async 函数的。

koa-bodyparser 中心件模仿

想要剖析 koa-bodyparser 的原理首要需求知道用法和效果,koa-bodyparser 中心件是将咱们的 post无敌女夫子 恳求和表单提交的查询字符串转换成目标,并挂在 ctx.request.body 上,便利咱们在其他中心件或接口处取值,运用前需提早装置。


npm install koa koa-bodyparser

koa-bodyparser 详细用法如下:

koa-bodyparser 的用法


const Koa = require('koa');
const bodyParser = require('koa-bodyparser');
const app = new Koa();
// 运用中心件
app.use(bodyParser());
app.use(async (ctx, next) => {
if (ctx.path === '/' && ctx.method === 'POST') {
// 运用中心件后 ctx.request.body 特色主动加上了 post 恳求的数据
console.log(ctx.request.body);
}
});
app.listen(3000);

依据用法咱们能够看出 koa-bodyparser 中心件引进的其实是一个函数,咱们把它放在了 use 中履行,依据 Koa 的特色,咱们推断出 koa-bodyparser 的函数履行后应该给咱们回来了一个 async 函数,下面是咱们模仿完结的代码。

文件:my-koa-bodyparser.js


const querystring = require('querystring');
module.exports = function bodyParser() {
return async (ctx, next) => {
await new Promise((resolve, reject) => {
// 存储数据的数组
const dataArr = [];
// 接纳钱文挥数据
ctx.req.on('data', data => dataArr.push(data));
// 整合数据并运用 Promise 成功
ctx.req.on('end', () => {
// 获取恳求数据的类型 json 或表单
const cmg08式马克沁重机枪ontentType = ctx.get('Content-Type');
// 获取数据 Buffer 格局
const data = Buffer.concat(dataArr).toString();
if (contentType === 'application/x-www-form-urlencoded') {
// 假如是表单提交,则将查询字符串转换成目标赋值给 ctx.request.body
ct江州二院x.request.body = querystring.parse(data);
} else if (contentType === 'applaction/json') {
// 假如是 json,则将字符串格局的目标转换成目标赋值给 ctx.request.body
ctx.request.body = JSON.parse(data);
}
// 履行成功的回调
resolve();
});
});
// 持续向下履行
await next();
}
}

在上面代码中由几点是需求咱们留意的,即 next 的调用以及为什么经过流接纳数据、处理数据和将数据挂在 ctx.request.body 要在 Promise 中进行。

首要是 next 的调用,咱们知道 Koa 的 next 履行,其实便是在履行下一个中心件的函数,即下一个 use 中的 async 函数,为了确保后边的异步代码履行结束后再持续履行当时的代码,所以咱们需求运用 await 进行等候,其次便是数据从接纳到挂在 ctx.request.body 都在 Promise 中履行,是因为在接纳数据的操作是异步的,整个处理数据的进程需求等候异步完结后,再把数据挂在 ctx.request.body 上,能够确保咱们鄙人一个 use 的 async 函数中能够在 ctx.request.body 上拿到数据,所以咱们运用 await 等候一个 Promise 成功后再履行 next。

koa-better-body 中心件模仿

koa-bodyparser 在处理表单提交时仍是显得有一点弱,因为不支撑文件上传,而 koa-better-body 则弥补了这个缺乏,可是 koa-better-body 为 Koa 1.x 版别的中心件,Koa 1.x 的中心件都是运用 Generator 函数完结的,咱们需求运用 koa-convert 将 koa-better-body 转化成 Koa 2.x 的中心件。


npm install koa koa-better-body koa-convert path uuid

koa-better-body 详细用法如下:

koa-better-body 的用法


const Koa = require('koa');
const betterBody = require('koa-better-body');二元一次方程组,精油按摩,曾舜晞-雷竞技安卓_雷竞技安卓版_雷竞技官方app下载安卓版
const convert = require('koa-convert'); // 将 koa 1.0 中心转化二元一次方程组,精油按摩,曾舜晞-雷竞技安卓_雷竞技安卓版_雷竞技官方app下载安卓版成 koa 2.0 中心件
const path = require('path');
co二元一次方程组,精油按摩,曾舜晞-雷竞技安卓_雷竞技安卓版_雷竞技官方app下载安卓版nst fs = require('fs');
const uuid = require('uuid/v1'); // 生成随机串
const app = new Koa();
// 将 koa-better-body 中心件从 koa 1.0 转化成 koa 2.0,并运用中心件
app.use(convert(betterBody({
uploadDir: path.resolve(__dirname, 'upload')
}))巫婆造美人);
app.use(async (ctx, next) => {
if (ctx.path === '/' && ctx.method === 'POST') {
// 运用中心件后 ctx.request.fields 特色主动加上了 post 恳求的文件数据
console.log(ctx.request.fields);
// 将文件重命名
let imgPath = ctx.request.fields.avatar[0].path;
let newPath = path.resolve(__dirname, uuid());
fs.rename(imgPath, newPath);
}
});
app.listen(3000);

上面代码中 koa-better-body 的首要功用便是将表单上传的文件存入本地指定的文件夹下,并将文件流目标挂在了 ctx.request.fields 特色上,咱们接下来就模仿 koa-better-body 的功用完结一版依据 Koa 2.x 处理文件上传的中心件。

文件:my-koa-better-body.js


const fs = require('fs');
const uuid = require('uuid/v1');
const path = require('path');
// 给 Buffer 扩展 split 办法准备后边运用
Buffer.prototype.split = function (sep) {
let len = Buffer.from(sep).length; // 分隔符所占的字节数
let result = []; // 回来的数组
let start = 0; // 查找 Buffer 的开始方位
let offset = 0; // 偏移量
// 循环查找分隔符
while ((offset = this.indexOf(sep, start)) !== -1) {
// 将分隔符之前的部分截取出来存入
result.push(this.slice(start, offset));
start = offset + len;
}
// 处理剩余的部分
result.push(this.slice(start));
// 回来成果
return result;
}
module.exports = function (options) {
return async (ctx, next) => {
await new Promise((resolve, reject) => {
const dataArr = []; // 存储读取的数据
// 读取数据
ctx.req.on('data', data => dataArr.push(data));
ctx.req.on('end', () => {
// 取到恳求体每段的切割线字符串
let bondery = `--${ctx.get('content-Type').split('=')[1]}`;
// 获取不同体系的换行符
let lineBreak = process.platform === 'win32' ? '\r\n' : '\n';
// 非文件类型数据的终究回来贝丽岛成果
let fields = {};
// 分隔的 buffer 去掉没用的头和尾即最初的 '' 和结尾的 '--'
dataArr = dataArr.split(bondery).slice(1, -1);
// 循环处理 dataArr 中每一段 Buffer 的内容
dataArr.forEach(lines => {
// 关于一般值,信息由包括键名的行 + 两个换行 + 数据值 + 换行组成
// 关于文件,信息由包括 filename 的行 + 两个换行 + 文件内容 + 换行组成
let [head, tail] = lines.split(`${lineBreak}${lineBreak}`);
// 判别是否是文件,假如是文件则创立文件并写入,假如是一般值则存入 fields 目标中
if (head.includes('filename')) {
// 避免文件内容含有换行而被切割,应从头截取内容并去掉最终的换行
let tail = lines.slice(
head.length + 2 * lineBreak.length,
-lineBreak.length
);
// 创立可写流并指定写入的途径:
//二元一次方程组,精油按摩,曾舜晞-雷竞技安卓_雷竞技安卓版_雷竞技官方app下载安卓版 绝对途径 + 指定文件夹 + 随机文件名,最终写入文件
fs.createWriteStream(
path.join(__dirname, options.uploadDir, uuid())
).end(tail);
} else {
// 是一般值取出键名
let key = h江门野协ead.match(/name='(\w+)'/)[1];
// 将 key 设置给 fields tail 去掉结尾换行后的内容
fields[key] = tail.toString('utf8').slice(0, -lineBreak.length);
}
});
// 将处理好的 fields 目标挂在 ctx.request.fields 上,并完结 Promise
ctx.request.fields = fields;
resolve();
});
});
// 向下履行
await next();
}
}

上面的内容逻辑能够经过代码注释来了解,便是模仿 koa-better-body 的功用逻辑,咱们首要的关怀点在于中心件完结的办法,上面功用完结的异步操作依然是读取数据,为了等候数据处理结束仍然在 Promise 中履行,并运用 await 等候,Promise 履行成功调用 next。

koa-views 中心件模仿

Node 模板是咱们常常运用的东西用来在服务端帮咱们烘托页面,模板的品种繁复,因而呈现了 koa-view 中心件,帮咱们来兼容这些模板,先装置依靠的模块。


npm install koa koa-views ejs

下面是一个 ejs 的模板文件:

文件:index.ejs









<%= name %>
<%= age %>
<% if (name=='panda') { %>
panda
<% } else { %>
shen
<% } %>
<% arr.forEach(item => { %>
  • <%=item%>

  • <% }) %>


    koa-vi咬胸ews 详细用法如下:

    koa-views 的用法


    const Koa = require('koa');
    const views = require('koa-views');
    const path = require('path');
    const app = new Koa();
    // 运用中心件
    app.use(views(path.resolve(__dirname, 'views'), {
    extension: 'ejs'
    }));
    app.use(async (ctx, next) => {
    await ctx.render('index', { name: 'panda', age: 20, arr: [1, 2, 3] });
    });
    app.listen(3000);

    能够看出咱们运用了 koa-views 中心件后,让 ctx 上多了 render 办法协助咱们完结对模板的烘托和呼应页面,就和直接运用 ejs 自带的 render 办法相同,而且从用法能够看出 render 办法是异步履行的,所以需求运用 await 进行等候,接下来咱们就来模仿完结一版简略的 koa-views 中心件。

    文件:my-koa-views.js


    const fs = require('fs');
    const path = require('path');
    const { promisify } = require('util');
    // 将读取文件办法转换成 Promise
    const readFile = promisify(fs.readFile);
    // 处处中心件
    module.exports 穆姜传= function (dir, options) {
    return async (ctx, next) => {
    // 动态引进模板依靠模块
    const view = require(options.extension);
    ctx.render = async (filename, data) => {
    // 异步读取文件内容
    let tmpl = await readFile(
    path.join(dir, `${filename}.${options.extension}`),
    'utf8'
    );
    // 叙组词将模板烘托并回来页面字符串
    let pageStr 二元一次方程组,精油按摩,曾舜晞-雷竞技安卓_雷竞技安卓版_雷竞技官方app下载安卓版= view.render(tmpl, data);
    // 设置呼应类型并呼应页面
    ctx.set('Content-Type', 'text/html;charset=utf8');
    ctx.body = pageStr;
    }
    // 持续向下履行
    await next();
    }
    }

    挂在 ctx 上的 render 办法之所以是异步履行的是因为内部读取模板文件是异步履行的,需求等候,所以 render 办法为 async 函数,在中心件内部动态引藏保涂入了咱们使的用模板,如 ejs,并在 ctx.render 内部运用对应的 render 办法获取替换数据后的页面字符串,并以 html 的类型呼应。

    koa-static 中心件模仿

    下面是 koa-static 中心件的用法,代码运用的依靠如下,运用前需装置基督山伯爵之伯爵夫人。


    npm install koa koa-static mime

    koa-static 详细用法如下:

    koa-static 的用法


    const Koa = require('koa');
    const static = require('koa-static');
    const path = require('path');
    const app = new Koa();
    app.use(stat孔今辉ic(path.resolve(__dirname, 'public')));
    app.use(async (ctx, next) => {
    ctx.body = 'hello world';
    });
    app.listen(3000);

    经过运用和剖析,咱们知道了 koa-static 中心件的效果是在服务器接到恳求时,帮咱们处理静态文件,假如咱们直接拜访文件名的时分,会查找这个文件并直接呼应,假如没有这个文件途径会当作文件夹,并查找文件夹下的 index.html,假如存在则直接呼应,假如不存在则交给其他中心件处理夫前。

    文件:my-koa-static.js


    const fs = require('fs');
    const path = require('path');
    const mime = require('mime');
    const { promisify } = require('util');
    // 将 stat 和 access 转换成 Promise
    const stat = promisify(fs.stat);
    const access = promisify(fs.access)
    module.exports = function (dir) {
    return async (ctx, next) => {
    // 将拜访的路由处理成绝对途径,这儿要运用 join 因为有可能是 /
    let realPath = path.join(dir, ctx.path);
    try {
    // 获取 stat 目标
    let statObj = await stat(realPath);
    // 假如是文件,则设置文件类型并直接呼应内容,不然当作文件夹寻觅 index.html
    if (statObj.isFile()) {
    ctx.set('Content-Type', `${mime.getType()};charset=utf8`);
    ctx.body = fs.createReadStream(realPath);
    } else {
    let filename = path.join(realPath, 'index.html');
    // 假如不存在该文件则履行 catch 中的 next 交给其他中心件处理
    await access(filename);
    // 存在设置文件类型并呼应内容
    ctx.set('Content-Type', 'text/html;charset=utf8');
    ctx.body = fs.createReadStream(filename);
    }
    } catch (e) {
    await next();
    }
    }
    }

    上面的逻辑中需求检测途径是否存在,因为咱们导出的函数都是 async 函数,所以咱们将 stat 和 access 转化成了 Promise,并用 try...catch 进行捕获,在途径不合法时调用 next 交给其他中心件处理。

    koa-router 中心件模仿

    在 Express 结构中,路由是被内置在了结构内部,而 Koa 中没有内置,是运用 koa-router 中心件来完结的,运用前需求装置。


    npm install koa koa-router

    koa-router 功用十分强壮,下面咱们仅仅简略的运用,而且依据运用的功用进行模仿。

    koa-router 的简略用法


    const Koa = require('Koa');
    const Rout妈妈卖淫er = require('koa-router');
    const app = new Koa();
    const router = new Router();
    router.get('/panda', (ctx, next) => {
    ctx.body = 'panda';
    });
    router.get('/pandashen', (ctx, next) => {
    ctx.body = 'pandashen';
    });
    router.get('/shen', (ctx, next) => {
    ctx.body = 'shen';
    })
    // 调用路由中心件
    app.use(router.routes());
    app.listen(3000);

    从上面看出 koa-router 导出的是一个类,运用时需求创立一个实例,而且调用实例的 routes 办法将该办法回来的 async 函数进行衔接,可是在匹配路由的时分,会依据路由 get 办法中的途径进行匹配,并串行履行内部的回调函数,当一切回调函数履行结束之后会履行整个 Koa 串行的 next,原理同其他中心件,我下面来针对上面运用的功用简易完结。

    文件:my-koa-router.js


    // 操控每一个路由层的类
    class Layer {
    constructor(path, cb) {
    this.path = path;
    this.cb = cb;
    }
    match(path) {
    // 地址的路由和当时装备路由持平回来 true,不然回来 false
    return path === this.path;
    }
    }
    // 路由的类
    class Router {
    constructor() {
    // 寄存每个路由目标的数组,{ path: /xxx, 二元一次方程组,精油按摩,曾舜晞-雷竞技安卓_雷竞技安卓版_雷竞技官方app下载安卓版fn: cb }
    this.layers = [];
    }
    get(path, cb) {
    // 将路由目标存入数组中
    this.layers.push(new Layer(path, cb));
    }
    compose(ctx, next, handlers) {
    // 将匹配的路由函数串联履行
    function dispatch(index) {
    // 假如当时 index 个数大于了存储路由目标的长度,则履行 Koa 的 next 办法
    if (index >= handlers.length) return next();
    // 不然调用取出的路由目标的回调履行
    // 并传入一个函数,在传入的函数中递归 dispatch(index + 1)
    // 意图是为了履行下一个路由目标上的回调函数
    handlers[index].cb(ctx, () => dispatch(index + 1));
    }
    // 第一次履行路由目标的回调函数
    dispatch(0);
    }
    routes() {
    return async (ctx, next) {
    // 当时 next 是 Koa 自己的 next,即 Koa 其他的中心件
    // 挑选出途径相同的路由
    let handlers = this.layers.filter(layer => layer.match(ctx.path));
    this.compose(ctx, next, handlers);
    }
    }
    }

    在上面咱们创立了一个 Router 类,界说了 get 办法,当然还有 post 等,咱们只完结 get 意思一下,get 内为逻辑为将调用 get 办法的参白宁帝夜琛数函数和路由字符串一起构建成目标存入了数组 layers,所以咱们创立了专门结构路由目标的类 Layer,便利扩展,在路由匹配时咱们能够依据 ctx.path 拿到路由字符串,并经过该路由过七味铁屑丸滤调数组中与路由不匹配的路由目标,调用 compose 办法将过滤后的数组作为参数 handlers 传入,串行履行路由目标上的回调函数。

    compose 这个办法的完结思维十分的重要,在 Koa 源码中用于串联中心件,在 React 源码中用于串联 redux 的 promise、thunk 和 logger 等模块,咱们的完结是一个简版,并没有兼容异步,首要思维是递归 dispatch 函数,每次取出数组中下一个路由目标的回调函数履行,直到一切匹配的路由的回调函数都履行完,履行 Koa 的下一个中心件 next,留意此处的 next 不同于数组中回调函数的参数 next,数组中路由目标回调函数的 next 代表下一个匹配路由的回调。

    总结

    上面咱们剖析和模仿了一些中心件,其实咱们会了解 Koa 和 Express 相比较的优势是没有那么深重,开发运用便利,需求的功用都能够用对应的中心件来完结,运用中心件能够给咱们带来一些优点,比方能将咱们处理好的数据和新办法挂载在 ctx 上,便利后边 use 传入的回调函数中运用,也能够帮咱们处理一些公共逻辑,不至于在每一个 use 的回调中都去处理,大大减少了冗余代码,由此看来其实给 Koa 运用中心件的进程便是一个典型的 “装修器” 形式,在经过上面的剖析之后信任我们也了解了 Koa 的 “洋葱模型” 和异步特色,知道该怎么开发自己的中心件了。