Эта статья объяснитkoa
Модель лука, почему мы должны использовать модель лука и внедрить ее принципы. Оставьте модель лука для пониманияkoa
Это очень важно, надеюсь, эта статья поможет вам~
Какая модель лука
давайте посмотрим одинdemo
const Koa = require('koa');
const app = new Koa();
// 中间件1
app.use((ctx, next) => {
console.log(1);
next();
console.log(2);
});
// 中间件 2
app.use((ctx, next) => {
console.log(3);
next();
console.log(4);
});
app.listen(8000, '0.0.0.0', () => {
console.log(`Server is starting`);
});
Результат:
1
3
4
2
существуетkoa
промежуточное ПОnext()
Метод разделен на две части.next()
Верхняя часть метода будет выполняться первой, а нижняя часть будет выполняться после того, как будут завершены все последующие выполнения промежуточного программного обеспечения. Это хорошо видно из следующего рисунка:
В луковой модели каждый уровень эквивалентен промежуточному программному обеспечению, которое выполняет определенные функции, такие как обработка ошибок,Session
обработка и так далее. Порядок обработки первыйnext()
предварительный запрос (Request
, от внешнего слоя к внутреннему слою), а затем выполнитьnext()
функцию и, наконец,next()
пост-ответ (Response
, от внутреннего к внешнему), т.Каждое промежуточное ПО имеет две возможности обработки.
Почему Koa использует модель onion
Если это не луковичная модель, что нам делать, если наше промежуточное ПО зависит от логики другого промежуточного ПО?
Например, нам нужно знать запрос или операциюdb
Сколько времени это займет, и хотите получить информацию о другом промежуточном программном обеспечении. существуетkoa
, мы можем использоватьasync await
способ в сочетании с моделью лука, чтобы сделать это.
app.use(async(ctx, next) => {
const start = new Date();
await next();
const delta = new Date() - start;
console.log (`请求耗时: ${delta} MS`);
console.log('拿到上一次请求的结果:', ctx.state.baiduHTML);
})
app.use(async(ctx, next) => {
// 处理 db 或者进行 HTTP 请求
ctx.state.baiduHTML = await axios.get('http://baidu.com');
})
И это было бы невозможно без модели лука.
Погрузитесь в модель лука Коа
Начнем со статьиdemo
анализироватьkoa
Внутренняя реализация.
const Koa = require('koa');
//Applications
const app = new Koa();
// 中间件1
app.use((ctx, next) => {
console.log(1);
next();
console.log(2);
});
// 中间件 2
app.use((ctx, next) => {
console.log(3);
next();
console.log(4);
});
app.listen(9000, '0.0.0.0', () => {
console.log(`Server is starting`);
});
использовать метод
use
Способ состоит в том, чтобы делать одну вещь и поддерживать ееmiddleware
массив промежуточного ПО
use(fn) {
// ...
// 维护中间件数组——middleware
this.middleware.push(fn);
return this;
}
метод прослушивания и метод обратного вызова
воплощать в жизньapp.listen
метод, по сутиNode.js
Роднойhttp
модульcreateServer
метод создает службу, обратный вызов которойcallback
метод.callback
Метод находится в центре нашего внимания сегодняcompose
функция, возвращающая значениеPromise
функция.
listen(...args) {
debug('listen');
// node http 创建一个服务
const server = http.createServer(this.callback());
return server.listen(...args);
}
callback() {
// 返回值是一个函数
const fn = compose(this.middleware);
const handleRequest = (req, res) => {
// 创建 ctx 上下文环境
const ctx = this.createContext(req, res);
return this.handleRequest(ctx, fn);
};
return handleRequest;
}
handleRequest
будет казненcompose
возвращается функциейPromise
функцию и вернуть результат.
handleRequest(ctx, fnMiddleware) {
const res = ctx.res;
res.statusCode = 404;
const onerror = err => ctx.onerror(err);
const handleResponse = () => respond(ctx);
onFinished(res, onerror);
// 执行 compose 中返回的函数,将结果返回
return fnMiddleware(ctx).then(handleResponse).catch(onerror);
}
koa-compose
compose
Ссылка на функциюkoa-compose
эта библиотека. Его реализация выглядит так:
function compose (middleware) {
// ...
return function (context, next) {
// last called middleware #
let index = -1
// 一开始的时候传入为 0,后续会递增
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
// 当 fn 为空的时候,就会开始执行 next() 后面部分的代码
if (!fn) return Promise.resolve()
try {
// 执行中间件,留意这两个参数,都是中间件的传参,第一个是上下文,第二个是 next 函数
// 也就是说执行 next 的时候也就是调用 dispatch 函数的时候
return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
} catch (err) {
return Promise.reject(err)
}
}
}
}
Код очень простой, давайте посмотрим на конкретный поток выполнения:
Когда мы выполняем в первый раз, вызовdispatch(0)
, на этот раз я 0,fn
это первая промежуточная функция. И выполнить мидлвар, обратите внимание на эти два параметра, которые являются параметрами мидлвара, первый это контекст, второй этоnext
функция. то естьКогда промежуточное ПО выполняется следующим, это также вызывает функцию диспетчеризации., поэтому выполнениеnext
Причина, по которой следующее промежуточное ПО будет выполнено, когда логика:
return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
При втором и третьем исполненииdispatch
Когда , как и в первый раз, начнем выполнять второе и третье промежуточное программное обеспечение соответственно, выполнитеnext()
начинает выполнение следующего промежуточного программного обеспечения.
Когда выполняется третье промежуточное программное обеспечение, оно выполняется дляnext()
когда,dispatch
В функцию передается параметр 3,fn
дляundefined
. В это время он будет выполнен
if (!fn) return Promise.resolve()
В это время будет выполнено третье промежуточное ПО.next()
Код после этого, затем второй, первый, формируя таким образом модель лука.
Процесс выглядит следующим образом:
Простая версия компоновки
модельkoa
логики, мы можем написать упрощенную версиюcompose
. Чтобы облегчить ваше понимание:
const middleware = []
let mw1 = async function (ctx, next) {
console.log("next前,第一个中间件")
await next()
console.log("next后,第一个中间件")
}
let mw2 = async function (ctx, next) {
console.log("next前,第二个中间件")
await next()
console.log("next后,第二个中间件")
}
let mw3 = async function (ctx, next) {
console.log("第三个中间件,没有next了")
}
function use(mw) {
middleware.push(mw);
}
function compose(middleware) {
return (ctx, next) => {
return dispatch(0);
function dispatch(i) {
const fn = middleware[i];
if (!fn) return;
return fn(ctx, dispatch.bind(null, i+1));
}
}
}
use(mw1);
use(mw2);
use(mw3);
const fn = compose(middleware);
fn();
Суммировать
Koa
Луковая модель относится кnext()
Функция является точкой разделения и сначала выполняется снаружи внутрь.Request
логика, а затем выполнять изнутри наружуResponse
логика. Благодаря луковой модели становится более осуществимой и простой связь между несколькими промежуточными программами и так далее. Принцип его реализации не очень сложен, в основномcompose
метод.