считаю использованнымKoa,ReduxилиExpressВсе мои друзья знакомы с промежуточным программным обеспечением, особенно при изученииKoaв процессе, также вступит в контакт с«Луковая модель». В этой статье Brother A Bao будет изучать с вами промежуточное ПО Koa, но здесь Brother A Bao не планирует показывать общеизвестную информацию с самого начала.«Схема луковой модели», но сначала представим, что такое промежуточное ПО в Koa?
Прочитайте последние популярные статьи брата А Бао (спасибо за вашу поддержку и поддержку 🌹🌹🌹):
- Какие уроки можно извлечь из проекта 77.9K Star Axios?(895+ 👍)
- Используя эти идеи и методы, я прочитал множество отличных проектов с открытым исходным кодом.(591+ 👍)
- Что я узнал из 13 000 проектов с открытым исходным кодом(546+ 👍)
1. Промежуточное ПО Коа
существует@types/koa-compose
в упаковкеindex.d.ts
В заголовочном файле находим определение типа промежуточного ПО:
// @types/koa-compose/index.d.ts
declare namespace compose {
type Middleware<T> = (context: T, next: Koa.Next) => any;
type ComposedMiddleware<T> = (context: T, next?: Koa.Next) => Promise<void>;
}
// @types/koa/index.d.ts => Koa.Next
type Next = () => Promise<any>;
НаблюдаяMiddleware
Определение типа, мы можем знать, что в Коа промежуточное ПО — это обычная функция, которая получает два параметра:context
иnext
. вcontext
представляет объект контекста, аnext
Представляет объект функции, который при вызове возвращает объект Promise.
Поняв, что такое промежуточное ПО Koa, давайте представим ядро промежуточного ПО Koa, а именноcompose
функция:
function wait(ms) {
return new Promise((resolve) => setTimeout(resolve, ms || 1));
}
const arr = [];
const stack = [];
// type Middleware<T> = (context: T, next: Koa.Next) => any;
stack.push(async (context, next) => {
arr.push(1);
await wait(1);
await next();
await wait(1);
arr.push(6);
});
stack.push(async (context, next) => {
arr.push(2);
await wait(1);
await next();
await wait(1);
arr.push(5);
});
stack.push(async (context, next) => {
arr.push(3);
await wait(1);
await next();
await wait(1);
arr.push(4);
});
await compose(stack)({});
Источник приведенного выше кода:GitHub.com/Смотри, о, это/compo…
Для приведенного выше кода мы хотим выполнитьcompose(stack)({})
После оператора массивarr
значение[1, 2, 3, 4, 5, 6]
. Здесь нам все равноcompose
Как реализована функция. Разберем, нужен ли массивarr
Выведите желаемый результат, поток выполнения трех вышеуказанных промежуточных программ:
1. Начните выполнение первого промежуточного ПО и вставьте в массив arr 1. В это время значение массива arr равно[1]
, затем подождите 1 миллисекунду. Чтобы гарантировать, что первый элемент массива arr2
, нам нужно позвонитьnext
После функции начните выполнение второго промежуточного ПО.
2. Начните выполнение второго промежуточного ПО и вставьте в массив arr 2. В это время значение массива arr равно[1, 2]
, продолжайте ждать 1 миллисекунду. Чтобы убедиться, что второй элемент массива arr3
, нам также нужно позвонитьnext
После функции начните выполнение третьего промежуточного ПО.
3. Начните выполнение третьего промежуточного ПО и поместите в массив arr 3. В это время значение массива arr равно[1, 2, 3]
, продолжайте ждать 1 миллисекунду. Чтобы гарантировать, что третий элемент массива arr4
, требуем, чтобы в середине обращения к третьемуnext
После функции она должна иметь возможность продолжать выполнение.
4. При выполнении третьего промежуточного ПО значение массива arr равно[1, 2, 3, 4]
. Следовательно, чтобы убедиться, что четвертый элемент массива arr равен 5, нам нужно вернуть второе промежуточное ПО после выполнения третьего промежуточного ПО.next
После функции начинается выполнение оператора.
5. При выполнении второго промежуточного ПО значение массива arr равно[1, 2, 3, 4, 5]
. Точно так же, чтобы убедиться, что пятый элемент массива arr равен 6, нам нужно вернуться к первому промежуточному программному обеспечению после выполнения второго промежуточного программного обеспечения.next
После функции начинается выполнение оператора.
6. При выполнении первого промежуточного ПО значение массива arr равно[1, 2, 3, 4, 5, 6]
.
Для более интуитивного понимания описанного выше процесса выполнения мы можем рассматривать каждое промежуточное ПО как большую задачу, а затемnext
Функция является точкой разделения, и каждая большая задача разбирается на 3beforeNext
,next
иafterNext
3 небольших задания.
На приведенном выше рисунке мы начинаем с промежуточного программного обеспечения.beforeNext
Задача начнет выполняться, а затем следуйте шагам выполнения, указанным фиолетовой стрелкой, чтобы завершить планирование задач промежуточного программного обеспечения. существуетКакие уроки можно извлечь из проекта 77.9K Axios?В этой статье брат А Бао изРегистрация задач, планирование задач и планирование задач3 аспекта для анализаAxiosРеализация перехватчика. Точно так же Brother Abao проанализирует механизм промежуточного программного обеспечения Koa с точки зрения трех вышеупомянутых аспектов.
1.1 Регистрация задачи
В Koa, после создания объекта приложения Koa, мы можем вызвать объектuse
способ регистрации промежуточного ПО:
const Koa = require('koa');
const app = new Koa();
app.use(async (ctx, next) => {
const start = Date.now();
await next();
const ms = Date.now() - start;
console.log(`${ctx.method} ${ctx.url} - ${ms}ms`);
});
фактическиuse
Реализация метода очень проста, т.lib/application.jsфайл, находим его определение:
// lib/application.js
module.exports = class Application extends Emitter {
constructor(options) {
super();
// 省略部分代码
this.middleware = [];
}
use(fn) {
if (typeof fn !== 'function') throw new TypeError('middleware must be a function!');
// 省略部分代码
this.middleware.push(fn);
return this;
}
}
Как видно из приведенного выше кода, вuse
Внутри метода будетfn
Параметр проверяется на тип, при прохождении проверкиfn
Промежуточное ПО, на которое указывает, сохраняется вmiddleware
массив, а также возвращаетthis
объект, который поддерживает цепочку вызовов.
1.2 Планирование задач
существуетКакие уроки можно извлечь из проекта 77.9K Axios?В этой статье Brother Abao ссылается на модель конструкции перехватчика Axios и описывает следующие общие модели обработки задач:
В этой общей модели Brother Abao выполняет планирование задач, размещая препроцессор и постпроцессор до и после основных задач CoreWork соответственно. Для механизма промежуточного программного обеспечения Koa это достигается размещением препроцессора и постпроцессора соответственно вawait next()
До и после утверждения, чтобы завершить расположение задачи.
// 统计请求处理时长的中间件
app.use(async (ctx, next) => {
const start = Date.now();
await next();
const ms = Date.now() - start;
console.log(`${ctx.method} ${ctx.url} - ${ms}ms`);
});
1.3 Планирование задач
Из предыдущего анализа мы уже знаем, что использованиеapp.use
Промежуточное ПО, зарегистрированное методом, будет сохранено во внутреннемmiddleware
в массиве. Чтобы завершить планирование задач, нам нужно постоянно начинать сmiddleware
Выньте промежуточное ПО из массива для выполнения. Алгоритм планирования промежуточного программного обеспечения инкапсулирован вkoa-composeв упаковкеcompose
В функции конкретная реализация функции выглядит следующим образом:
/**
* Compose `middleware` returning
* a fully valid middleware comprised
* of all those which are passed.
*
* @param {Array} middleware
* @return {Function}
* @api public
*/
function compose(middleware) {
// 省略部分代码
return function (context, next) {
// last called middleware #
let index = -1;
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;
if (!fn) return Promise.resolve();
try {
return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
} catch (err) {
return Promise.reject(err);
}
}
};
}
compose
Функция принимает один параметр, тип параметра — массив, и при вызове функции возвращается новая функция. Далее мы возьмем предыдущий пример в качестве примера для анализаawait compose(stack)({});
Исполнение заявления.
1.3.1 dispatch(0)
Как видно из рисунка выше, при вызове первого промежуточного ПОnext
функция, по сути, продолжает вызыватьdispatch
функция, параметры в это времяi
значение1
.
1.3.2 dispatch(1)
Как видно из рисунка выше, при вызове второго промежуточного ПО внутриnext
функция, все еще вызывающаяdispatch
функция, параметры в это времяi
значение2
.
1.3.3 dispatch(2)
Как видно из рисунка выше, при вызове третьего мидлвара внутриnext
функция, все еще вызывающаяdispatch
функция, параметры в это времяi
значение3
.
1.3.4 dispatch(3)
Как видно из приведенного выше рисунка, когдаmiddleware
После того, как промежуточное ПО в массиве начнет выполняться, если планирование не задано явноnext
значение параметра, он начнет возвращатьnext
Оператор после функции продолжает выполняться. Когда третье промежуточное ПО будет выполнено, оно вернется ко второму промежуточному ПО.next
Операторы после функции продолжают выполняться до тех пор, пока не будут выполнены все операторы, определенные в промежуточном программном обеспечении.
После анализаcompose
Код реализации функции, давайте посмотрим, как Koa использует его внутриcompose
Функция для обработки зарегистрированного промежуточного ПО.
const Koa = require('koa');
const app = new Koa();
// 响应
app.use(ctx => {
ctx.body = '大家好,我是阿宝哥';
});
app.listen(3000);
Используя приведенный выше код, я могу быстро запустить сервер. вuse
Мы уже разобрали этот метод ранее, так что давайте проанализируем его далее.listen
метод, реализация которого заключается в следующем:
// lib/application.js
module.exports = class Application extends Emitter {
listen(...args) {
debug('listen');
const server = http.createServer(this.callback());
return server.listen(...args);
}
}
очевидно вlisten
Внутри метода он сначала вызовет встроенный HTTP-модуль Node.js.createServer
способ создать сервер, а затем начать слушать указанный порт, то есть начать ждать клиентских подключений. Кроме того, при вызовеhttp.createServer
Когда метод создает HTTP-сервер, мы передаем следующие параметры:this.callback()
, конкретная реализация этого метода выглядит следующим образом:
// lib/application.js
const compose = require('koa-compose');
module.exports = class Application extends Emitter {
callback() {
const fn = compose(this.middleware);
if (!this.listenerCount('error')) this.on('error', this.onerror);
const handleRequest = (req, res) => {
const ctx = this.createContext(req, res);
return this.handleRequest(ctx, fn);
};
return handleRequest;
}
}
существуетcallback
Внутри метода мы, наконец, видим давно потерянноеcompose
метод. при звонкеcallback
После метода он вернетhandleRequest
Функциональные объекты используются для обработки HTTP-запросов. Вызывается всякий раз, когда сервер Koa получает запрос клиента.handleRequest
метод, который сначала создаст новый объект Context, а затем выполнит зарегистрированное промежуточное ПО для обработки полученного HTTP-запроса:
module.exports = class Application extends Emitter {
handleRequest(ctx, fnMiddleware) {
const res = ctx.res;
res.statusCode = 404;
const onerror = err => ctx.onerror(err);
const handleResponse = () => respond(ctx);
onFinished(res, onerror);
return fnMiddleware(ctx).then(handleResponse).catch(onerror);
}
}
Хорошо, содержание промежуточного программного обеспечения Koa в основном было представлено, те, кто интересуется ядром Koa, могут изучить его самостоятельно. Далее мы представим модель лука и ее приложения.
Следуйте «Дорога полного стека», чтобы прочитать другие статьи по анализу исходного кода и более 50 учебных пособий «Relearn TS».
Луковая модель
2.1 Введение в луковую модель
(Источник изображения:яйцо js.org/ru/intro/spoof…
На приведенном выше рисунке каждый слой в луковице представляет собой независимое промежуточное ПО для реализации различных функций, таких как обработка исключений, обработка кэша и т. д. Каждый запрос будет проходить через промежуточное ПО слой за слоем слева, и после входа в самое внутреннее промежуточное ПО будет возвращаться слой за слоем, начиная с самого внутреннего промежуточного ПО. Таким образом, для каждого слоя промежуточного ПО взапрос и ответВ цикле есть две временные точки для добавления различной логики обработки.
2.2 Применение луковой модели
Помимо применения луковой модели в Koa, модель также широко используется в некоторых хороших проектах на Github, таких какkoa-routerи Alibabamidway,umi-requestи т.д. проекты.
После представления промежуточного программного обеспечения и луковой модели Koa брат Абао выводит следующие общие модели обработки задач в соответствии со своим пониманием:
Промежуточное программное обеспечение, описанное на рисунке выше, обычно представляет собой общий код функции, не имеющий ничего общего с бизнесом, например промежуточное программное обеспечение, используемое для установки времени отклика:
// x-response-time
async function responseTime(ctx, next) {
const start = new Date();
await next();
const ms = new Date() - start;
ctx.set("X-Response-Time", ms + "ms");
}
Для каждого промежуточного программного обеспечения препроцессор и постпроцессор являются необязательными. Например, для установки унифицированного содержимого ответа используется следующее промежуточное ПО:
// response
async function respond(ctx, next) {
await next();
if ("/" != ctx.url) return;
ctx.body = "Hello World";
}
Хотя два промежуточных ПО, описанных выше, относительно просты, вы также можете реализовать сложную логику в соответствии со своими потребностями. Ядро Коа очень легкое, а воробей маленький и цельный. Предоставляя элегантный механизм промежуточного программного обеспечения, он позволяет разработчикам гибко расширять функции веб-сервера.Эта дизайнерская идея достойна нашего изучения и ссылки. Хорошо, на этот раз я сначала представлю это здесь, если будет возможность, позже брат Абао представит это отдельно.ReduxилиExpressпромежуточный механизм.