Что такое koa framework?
koa — это новый веб-фреймворк, основанный на узле, созданный оригинальной командой Express Framework. Для него характерна элегантность, простота, выразительность и свобода. По сравнению с экспрессом, это более легкая структура узлов, поскольку все ее функции реализуются через плагины.Этот шаблон проектирования архитектуры подключаемых модулей соответствует философии Unix.
The koa framework has now been updated to version 2.x. This article starts from scratch, step by step, explains the source code structure and implementation principle of the koa2 framework, shows and explains some of the most important concepts in the source code of the koa2 framework, and then teaches you how to implement a simple The koa2 framework of koa2 helps everyone to learn and understand koa2 in a deeper level. After reading this article, go to check the source code of koa2, I believe your thinking will be очень гладкий.
В этой статье используется платформа koa2, которая отличается от koa1. koa1 использует метод выполнения генератора + co.js, в то время как koa2 использует async/await, поэтому код и демонстрация в этой статье должны работать на версии узла 8 и Если версия узла читателя ниже, рекомендуется обновить или установить babel-cli и использовать в нем узел babel для запуска кода, задействованного в этой статье.
Полный адрес кода gitlab облегченной версии koa, реализованной в этой статье:article_koa2
структура исходного кода koa
На изображении выше показана папка lib исходной структуры каталогов koa2.Папка lib содержит четыре основных файла koa2: application.js, context.js, request.js, response.js.
application.js
application.js — это входной файл koa.Он экспортирует конструктор для создания экземпляров класса.Он наследует события, что дает фреймворку возможность отслеживать события и запускать события. Приложение также предоставляет некоторые часто используемые API, такие как toJSON, listen, use и т. д.
Принцип реализации listen на самом деле является инкапсуляцией http.createServer, основное внимание уделяется обратному вызову, передаваемому в этой функции, который включает слияние промежуточного программного обеспечения, обработку контекста и специальную обработку res.
использование заключается в сборе промежуточного программного обеспечения, помещении нескольких промежуточного программного обеспечения в очередь кэширования, а затем рекурсивном объединении и вызове этих серий промежуточного программного обеспечения через плагин koa-compose.
context.js
Эта часть представляет собой контекст приложения ctx из koa. По сути, это просто экспозиция объекта. Акцент делается на делегате. Это прокси. Это сделано для удобства разработчиков. Например, мы хотим получить доступ к ctx. repponse.status, но мы передаем делегат. , вы можете получить к нему прямой доступ, обратившись к ctx.status.
запрос.js, ответ.js
Эти две части представляют собой некоторые операции над собственными res и req, использующие множество синтаксиса get и set ES6, для получения заголовков или установки заголовков, установки тела и т. д., они не будут подробно представлены, если вы заинтересованы Читатели могут сами посмотреть исходный код.
Четыре модуля для реализации koa2
Общая фреймворковая структура исходного кода koa2 кратко описана выше.Далее мы реализуем koa2 фреймворк.Автор считает, что для понимания и реализации koa framework требуется реализация четырех основных модулей, а именно:
- Узел Узел HTTP Server, создание конструктора класса KOA
- Создание запроса, ответа, объектов контекста
- Реализация механизма промежуточного программного обеспечения и модели очистки лука
- Перехват ошибок и обработка ошибок
- Ниже мы анализируем и реализуем один за другим.
Модуль 1: Инкапсуляция http-сервера node и создание конструктора класса Koa
Чтение исходного кода KOA2, мы знаем, что приложение сервера и мониторинг порта KOA фактически инкапсулированы на основе нативного кода узла. Код на следующем рисунке - это мониторинг сервера, реализованный нативным кодом узла.
let http = require('http');
let server = http.createServer((req, res) => {
res.writeHead(200);
res.end('hello world');
});
server.listen(3000, () => {
console.log('listenning on 3000');
});
Нам нужно инкапсулировать приведенный выше собственный код узла в режим koa:
const http = require('http');
const Koa = require('koa');
const app = new Koa();
app.listen(3000);
Первым шагом для реализации koa является инкапсуляция вышеуказанного процесса, для этого нам нужно создать application.js для реализации конструктора класса Application.
let http = require('http');
class Application {
constructor() {
this.callbackFunc;
}
listen(port) {
let server = http.createServer(this.callback());
server.listen(port);
}
use(fn) {
this.callbackFunc = fn;
}
callback() {
return (req, res) => {
this.callbackFunc(req, res);
};
}
}
module.exports = Application;
Затем создайте example.js, импортируйте application.js и запустите экземпляр сервера, чтобы запустить код мониторинга:
let Koa = require('./application');
let app = new Koa();
app.use((req, res) => {
res.writeHead(200);
res.end('hello world');
});
app.listen(3000, () => {
console.log('listening on 3000');
});
Теперь введите localhost:3000 в браузере, чтобы увидеть «hello world» в браузере. Теперь, когда первый шаг выполнен, мы просто инкапсулировали http-сервер и создали класс, который может генерировать экземпляры koa.Этот класс также реализует app.use для регистрации промежуточного программного обеспечения и регистрации функций обратного вызова, app.listen используется для запуска сервера. instance и передать функцию обратного вызова.Первый модуль в основном реализует типичный стиль koa и устанавливает простой фрейм koa. Далее мы начинаем писать и объяснять второй модуль.
Модуль 2: Создание запроса, ответа, объектов контекста
Читая исходный код koa2, мы знаем, что три файла context.js, request.js и response.js являются файлами кода трех модулей request, response и context соответственно. Контекст — это ctx, когда мы обычно пишем код koa.Он эквивалентен глобальному контексту экземпляра koa this, который соединяет два функциональных модуля запроса и ответа и подвергается воздействию параметров функций обратного вызова, таких как экземпляры koa и промежуточное ПО. на связующую роль.
Два функциональных модуля запроса и ответа соответственно инкапсулируют функцию собственного запроса и ответа узла, используя атрибуты getter и setter, а объект req/res, основанный на объекте node, инкапсулирует объект запроса/ответа koa. Исходя из этого принципа, мы просто реализуем request.js и response.js, сначала создаем файл request.js, а затем пишем следующий код:
let url = require('url');
module.exports = {
get query() {
return url.parse(this.req.url, true).query;
}
};
Таким образом, когда вы используете CTX.Query в экземпляре KOA, вы вернете значение Url.Parse (this.req.url, true).Query. Я вижу, что исходный код, основанный на геттере и сеттере, в Request.js, HEADER, URL, ORIGIN, PATH и т. д. также инкапсулированы геттером и сеттером, и автор здесь больше не реализован.
Затем мы реализуем модуль кода файла response.js, который совпадает с принципом запроса. Он также инкапсулирует собственный ответ на основе геттеров и сеттеров. Затем мы будем использовать два часто используемых оператора ctx.body и ctx.status как примеры Кратко опишите, если мы реализуем модуль ответа koa, мы сначала создаем файл response.js, а затем вводим следующий код:
module.exports = {
get body() {
return this._body;
},
set body(data) {
this._body = data;
},
get status() {
return this.res.statusCode;
},
set status(statusCode) {
if (typeof statusCode !== 'number') {
throw new Error('something wrong!');
}
this.res.statusCode = statusCode;
}
};
Вышеприведенный код реализует чтение и установку статуса koa.При чтении он возвращает атрибут statusCode на основе собственного объекта ответа, а чтение тела заключается в чтении, записи и работе с this._body. Нативный this.res.end здесь не используется для операции с телом, потому что, когда мы пишем код koa, тело будет считываться и изменяться много раз, поэтому фактическая операция возврата информации о браузере находится в пакете application.js и обрабатывается им. .
Теперь мы реализовали request.js и response.js и получили объекты запроса и ответа и их методы инкапсуляции.Затем начинаем реализовывать context.js.Функция контекста заключается в монтировании объектов запроса и ответа на ctx.Разрешить koa и код для простого использования методов в объектах запроса и ответа. Теперь мы создаем файл context.js и вводим следующий код:
let proto = {};
function delegateSet(property, name) {
proto.__defineSetter__(name, function (val) {
this[property][name] = val;
});
}
function delegateGet(property, name) {
proto.__defineGetter__(name, function () {
return this[property][name];
});
}
let requestSet = [];
let requestGet = ['query'];
let responseSet = ['body', 'status'];
let responseGet = responseSet;
requestSet.forEach(ele => {
delegateSet('request', ele);
});
requestGet.forEach(ele => {
delegateGet('request', ele);
});
responseSet.forEach(ele => {
delegateSet('response', ele);
});
responseGet.forEach(ele => {
delegateGet('response', ele);
});
module.exports = proto;
Файл context.js в основном используется для монтирования и проксирования часто используемых методов запроса и ответа, прямого проксирования context.request.query через context.query, context.body и context.status proxy context.response.body и context.response . положение дел. И context.request, context.response будут смонтированы в application.js.
Можно использовать простые сеттеры и геттеры для установки каждого метода, но поскольку метод определения объекта контекста относительно прост и стандартизирован, вы можете видеть в исходном коде koa, что исходный код koa использует __defineSetter__ и __defineSetter__ вместо setter/getter. Настройка чтения каждого атрибута предназначена в основном для облегчения расширения и упрощения метода записи.Когда нам нужно проксировать больше методов res и req, мы можем добавить соответствующее имя метода и req к объекту массива в файле context.js. имя.
На данный момент мы получили три объекта модуля запроса, ответа и контекста.Следующий шаг — смонтировать все методы запроса и ответа в контексте, чтобы контекст мог реализовать свою роль связующего звена между предыдущим и Далее Измените файл application.js и добавьте следующий код:
let http = require('http');
let context = require('./context');
let request = require('./request');
let response = require('./response');
createContext(req, res) {
let ctx = Object.create(this.context);
ctx.request = Object.create(this.request);
ctx.response = Object.create(this.response);
ctx.req = ctx.request.req = req;
ctx.res = ctx.response.res = res;
return ctx;
}
Как видите, мы добавили ключевой метод createContext, который создает ctx через Object.create, монтирует запрос и ответ в ctx, а также монтирует родные req и res в подсвойства ctx, оглянитесь назад на context/ request/response.js, и вы можете узнать, откуда взялся используемый в то время this.res или this.response.Оказалось, что соответствующий экземпляр был смонтирован в методе createContext.Выше, после построения контекста выполнения ctx, все параметры нашей функции обратного вызова app.use основаны на ctx.
Модуль 3: Реализация механизма промежуточного программного обеспечения и модели очистки лука
До сих пор мы успешно реализовали объект контекста, объект запроса и модуль объекта ответа.Наиболее важным модулем является модуль промежуточного программного обеспечения koa.Механизм промежуточного программного обеспечения koa представляет собой модель очистки лука.Промежуточное программное обеспечение помещается в очередь массива посредством использования а затем выполняется из внешнего слоя.После встречи с next он входит в следующий мидлвар в очереди.После того как все миддлвары выполнены он начинает возвращаться к фрейму,и выполняет в очереди код который не выполнялся в мидлваре до этого Часть, это модель очищающего лука, механизм промежуточного программного обеспечения koa.
Модель koa с очисткой лука реализована с помощью генератора + co.js в koa1 и реализована с помощью async/await + Promise в koa2 Далее мы реализуем механизм промежуточного программного обеспечения в koa2 на основе async/await + Promise . Во-первых, если предположить, что когда промежуточный механизм koa будет готов, он сможет успешно запустить следующий код:
let Koa = require('../src/application');
let app = new Koa();
app.use(async (ctx, next) => {
console.log(1);
await next();
console.log(6);
});
app.use(async (ctx, next) => {
console.log(2);
await next();
console.log(5);
});
app.use(async (ctx, next) => {
console.log(3);
ctx.body = "hello world";
console.log(4);
});
app.listen(3000, () => {
console.log('listenning on 3000');
});
```
运行成功后会在终端输出123456,那就能验证我们的koa的剥洋葱模型是正确的。接下来我们开始实现,修改application.js文件,添加如下代码:
```javascript
compose() {
return async ctx => {
function createNext(middleware, oldNext) {
return async () => {
await middleware(ctx, oldNext);
}
}
let len = this.middlewares.length;
let next = async () => {
return Promise.resolve();
};
for (let i = len - 1; i >= 0; i--) {
let currentMiddleware = this.middlewares[i];
next = createNext(currentMiddleware, next);
}
await next();
};
}
callback() {
return (req, res) => {
let ctx = this.createContext(req, res);
let respond = () => this.responseBody(ctx);
let onerror = (err) => this.onerror(err, ctx);
let fn = this.compose();
return fn(ctx);
};
}
```
koa通过use函数,把所有的中间件push到一个内部数组队列this.middlewares中,剥洋葱模型能让所有的中间件依次执行,每次执行完一个中间件,遇到next()就会将控制权传递到下一个中间件,下一个中间件的next参数,剥洋葱模型的最关键代码是compose这个函数:
```javascript
compose() {
return async ctx => {
function createNext(middleware, oldNext) {
return async () => {
await middleware(ctx, oldNext);
}
}
let len = this.middlewares.length;
let next = async () => {
return Promise.resolve();
};
for (let i = len - 1; i >= 0; i--) {
let currentMiddleware = this.middlewares[i];
next = createNext(currentMiddleware, next);
}
await next();
};
}
Роль функции createNext состоит в том, чтобы передать следующее промежуточное программное обеспечение предыдущего промежуточного программного обеспечения в качестве параметра следующему промежуточному программному обеспечению и привязать контекст ctx к текущему промежуточному программному обеспечению.Когда промежуточное программное обеспечение выполняется и вызывается next(), на самом деле выполнить следующее промежуточное ПО.
for (let i = len - 1; i >= 0; i--) {
let currentMiddleware = this.middlewares[i];
next = createNext(currentMiddleware, next);
}
Вышеприведенный код на самом деле является реализацией цепной обратной рекурсивной модели.Я начинаю цикл с максимального номера и инкапсулирует промежуточное ПО с последнего.Каждый раз он инкапсулирует свою собственную функцию выполнения в следующую как предыдущую середину.Следующий параметр программного обеспечения, поэтому при переходе к первому промежуточному ПО вам нужно только один раз выполнить next(), чтобы рекурсивно вызвать все промежуточное ПО в цепочке.Это основной механизм кода коа, очищающий лук.
Здесь мы суммируем весь код описанной выше процедуры очистки луковой модели, с помощью промежуточного программного обеспечения передается в функцию обратного вызова, параметром функции обратного вызова является передача контекста строки ctx, а следующее, следующее по факту управление, следующее действие — остановить запуск текущее промежуточное программное обеспечение, управление следующим промежуточным программным обеспечением, промежуточное программное обеспечение, следующее выполнение кода перед следующим (), следующий код промежуточного программного обеспечения, который запускается, встречается со следующим () и выполняет код справа от следующего промежуточного программного обеспечения, когда последнее промежуточное программное обеспечение выполнения, управление отменяется, вернитесь назад и начните выполнение кода до того, как все промежуточное программное обеспечение останется невыполненным, весь этот процесс немного похож на псевдорекурсивный, когда все промежуточные окончательные все части выполнены, он возвращает объект Promise, потому что мы составляющая функция возвращает функцию асинхронной, Функция асинхронного выполнения возвращает завершенное обещание, поэтому мы все можем выполнить асинхронную синхронизацию промежуточного программного обеспечения, а затем вы можете выполнить функцию обработки ошибок и функцию ответа.
После того, как код механизма промежуточного программного обеспечения написан, запустив наш пример выше, он может вывести 123456. На данный момент базовая структура нашего koa в основном завершена, но структура может не только реализовывать функции для экземпляров платформы и сервера Robust, но также необходимо добавить механизм обработки ошибок.
Модуль 4: Отлов ошибок и обработка ошибок
Для реализации базового фреймворка необходимы обработка и захват ошибок.Надежный фреймворк должен гарантировать, что при возникновении ошибок ошибки и генерируемые исключения могут быть перехвачены, возвращены и отправлены в систему мониторинга для обработки.Обратная связь, простая инфраструктура koa, которую мы реализовали не удалось этого добиться, мы добавим механизм обработки и захвата ошибок.
throw new Error('oooops');
Основываясь на текущей структуре, если в коде промежуточного программного обеспечения возникает указанное выше исключение ошибки, ошибка не может быть обнаружена.На данный момент давайте посмотрим на код возврата функции обратного вызова в application.js следующим образом:
return fn(ctx).then(respond);
Можно видеть, что fn — это функция выполнения промежуточного программного обеспечения, каждый код промежуточного программного обеспечения обернут асинхронной и функция выполнения, состоящая из промежуточного программного обеспечения, возвращает асинхронную функцию.Согласно спецификации es7 мы знаем, что асинхронный возвращает экземпляр объекта обещания A , если мы хотим поймать ошибки обещания, нам нужно использовать только метод catch обещания, чтобы перехватить все исключения промежуточного программного обеспечения.Измененный код возврата обратного вызова выглядит следующим образом:
return fn(ctx).then(respond).catch(onerror);
Теперь мы реализовали захват исключений ошибок промежуточного ПО, но нам все еще не хватает механизма захвата ошибок на уровне фреймворка.Мы надеемся, что наш экземпляр сервера может иметь механизм мониторинга событий ошибок, и мы можем подписаться и отслеживать уровень фреймворка через функцию слушателя.Ошибка выше, реализовать этот механизм несложно, просто используйте собственный модуль событий nodejs, модуль событий предоставляет нам функцию мониторинга событий и функцию поведения, запускающую событие, одну для передачи событий , и один для получения событий, нам нужно только изменить коа Конструктор может наследовать модуль событий Псевдокод после построения выглядит следующим образом:
let EventEmitter = require('events');
class Application extends EventEmitter {}
После наследования модуля событий, когда мы создаем экземпляр koa, добавляем функцию слушателя, код выглядит следующим образом:
let app = new Koa();
app.on('error', err => {
console.log('error happends: ', err.stack);
});
Таким образом, мы реализовали механизм захвата и мониторинга ошибок на уровне фреймворка. Подводя итог, обработка и захват ошибок делятся на захват обработки ошибок промежуточного программного обеспечения и захват обработки ошибок на уровне платформы, обработку ошибок промежуточного программного обеспечения с использованием обещания, обработку ошибок на уровне платформы с использованием событий собственного модуля nodejs, чтобы мы могли поместить все исключения ошибок на сервер. пойманы экземпляры. На данный момент мы полностью реализовали облегченную версию фреймворка koa.
конец
На данный момент мы реализовали облегченную версию фреймворка koa.Мы реализовали инкапсуляцию узла http сервера, создание конструктора класса Koa, построение запроса, ответа, объекта контекста, реализацию механизм промежуточного программного обеспечения и модель очистки лука, и захват ошибок.И четыре основных модуля обработки ошибок, поймите принцип реализации этой облегченной версии koa, а затем посмотрите на исходный код koa2, вы обнаружите, что все вдруг ясно , исходный код koa2 не что иное, как добавление на основе этой облегченной версии. Обработка многих функций и деталей инструмента не будет вводиться по одной из-за нехватки места.
Полный адрес кода gitlab облегченной версии koa, реализованной в этой статье:article_koa2
Автор: Первый головастик
github: статья будет опубликована впервыеПутешествие к сердцу Front-end Diaosi, добро пожаловать, чтобы отметить или посмотреть, спасибо
наконец,команда TNFEДля фронтенд-разработчиков мы отобрали самый свежий качественный контент в области небольших программ и веб-фронтальной техники, еженедельное обновление ✨, приветственная звезда, адрес github:GitHub.com/TNF E/TNF E-W…