если у тебя естьexpress
,koa
, redux
опыта, вы обнаружите, что у них есть中间件(middlewares)
Концепция чего-либо,中间件
Это идея перехватчика, который используется для добавления некоторой дополнительной обработки между конкретным входом и выходом, не влияя на исходную операцию.
первый контакт中间件
используется на стороне сервераexpress
а такжеkoa
, затем перешел от сервера к внешнему интерфейсу и увидел, чтоredux
Дизайн также получил большую игру.中间件
Идея дизайна также обеспечивает гибкую и мощную расширяемость для многих фреймворков.
Главное сравнение этой статьиredux
, koa
, express
Реализация промежуточного программного обеспечения, чтобы быть более интуитивно понятным, я извлеку три中间件
Соответствующий базовый код упрощается, и пишется пример моделирования. пример будет держатьexpress
, koa
,redux
Общая структура , постарайтесь сохранить такую же, как исходный код, поэтому в этой статье она также будет немного объяснена.express
, koa
, redux
Общая структура и ключевая реализация:
Пример адреса исходного кода, Вы можете читать статью, глядя на исходный код, добро пожаловать!
Эта статья подойдет разработчикам, имеющим определенное понимание и опыт работы с express, koa и redux.
промежуточное ПО на стороне сервера
express
а такжеkoa
Промежуточное ПО предназначено для обработкиhttp
запрос и ответ, но дизайнерские идеи у них действительно разные. большинство людей знаютexpress
а такжеkoa
Различия промежуточного программного обеспечения:
-
express
Используя метод «хвостовой рекурсии», промежуточное программное обеспечение выполняется один за другим последовательно, и оно привыкло кresponse
Ответ записывается в последнем промежуточном программном обеспечении; - а также
koa
поддержка промежуточного программного обеспеченияgenerator
, порядок выполнения - модель "луковичное кольцо".
Модель так называемого «лукового кольца»:
Но на самом деле,express
Промежуточное ПО также может формировать модель «луковичное кольцо», т.next
Код, написанный после вызова, также будет выполнен, ноexpress
Обычно этого не делают, потому чтоexpress
изresponse
Обычно в последнем промежуточном программном обеспечении, затем в другом промежуточном программном обеспеченииnext()
Последний код не влияет на окончательный результат ответа;
express
Первый взглядexpressРеализация:
Вход
// express.js
var proto = require('./application');
var mixin = require('merge-descriptors');
exports = module.exports = createApplication;
function createApplication() {
// app 同时是一个方法,作为http.createServer的处理函数
var app = function(req, res, next) {
app.handle(req, res, next)
}
mixin(app, proto, false);
return app
}
На самом деле здесь все очень просто, простоcreateApplication
метод созданияexpress
Например, обратите внимание на возвращаемое значениеapp
Это объект-экземпляр со множеством смонтированных на нем методов, а также сам метод, т.к.http.createServer
Функция обработки, конкретный код находится вapplication.jsсередина:
// application.js
var http = require('http');
var flatten = require('array-flatten');
var app = exports = module.exports = {}
app.listen = function listen() {
var server = http.createServer(this)
return server.listen.apply(server, arguments)
}
здесьapp.listen
передачаnodejs
изhttp.createServer
Создайтеweb
Услуги можно посмотреть здесьvar server = http.createServer(this)
вthis
которыйapp
сам, то настоящий обработчик, т.е.app.handle
;
Обработка промежуточного программного обеспечения
express
По сути, менеджер промежуточного программного обеспечения, при входеapp.handle
Это время, когда выполняется промежуточное программное обеспечение, поэтому две наиболее важные функции:
- app.handleПромежуточное ПО хвостового рекурсивного вызова для обработки req и res
- app.useДобавить промежуточное ПО
поддерживать глобальныйstack
Массив используется для хранения всего промежуточного программного обеспечения,app.use
Реализация очень проста, это может быть всего одна строка кода ``
// app.use
app.use = function(fn) {
this.stack.push(fn)
}
express
Конечно, реальная реализация не так проста, в ней есть встроенные функции маршрутизации, в т.ч.router
, route
, layer
Три ключевых класса, сrouter
быть правымpath
маневровый,stack
хранится вlayer
пример,app.use
Фактически вызываемый методrouter
примерuse
метода, те, кто заинтересован, могут прочитать его самостоятельно.
app.handle
Вот такstack
массив для обработки
app.handle = function(req, res, callback) {
var stack = this.stack;
var idx = 0;
function next(err) {
if (idx >= stack.length) {
callback('err')
return;
}
var mid;
while(idx < stack.length) {
mid = stack[idx++];
mid(req, res, next);
}
}
next()
}
Это то, что называется "хвостовой рекурсивный вызов",next
способ вывезтиstack
Функция "промежуточного программного обеспечения" в вызове, при этом помещаяnext
передается в «промежуточное программное обеспечение» в качестве третьего параметра, фиксированная форма каждого контракта промежуточного программного обеспечения(req, res, next) => {}
, так что каждой функции "промежуточного ПО" нужно вызывать толькоnext
метод может быть передан для вызова следующего промежуточного программного обеспечения.
Причина, по которой это называется «хвостовой рекурсией», заключается в том, что последний оператор рекурсивной функции должен вызвать саму функцию, поэтому последний оператор каждого промежуточного программного обеспечения должен бытьnext()
Для формирования "хвостовой рекурсии", иначе это обычная рекурсия.Преимущество "хвостовой рекурсии" перед обычной "рекурсией" в том, что она экономит место в памяти и не формирует глубоко вложенные стеки вызовов функций. Если вам интересно, вы можете прочитать книгу г-на Жуана.оптимизация хвостового вызова
Слишком далеко,express
Реализация промежуточного программного обеспечения завершена.
koa
Я должен сказать, по сравнению сexpress
С точки зрения,koa
Общий дизайн и реализация кода более совершенны и усовершенствованы; код основан наES6
внедрять, поддерживатьgenerator(async await)
, без встроенной реализации маршрутизации и какого-либо встроенного ПО промежуточного слоя,context
Дизайн тоже очень умный.
в общем и целом
Всего 4 файла:
- application.jsВходной файл, класс экземпляра приложения koa
-
context.js
ctx
Например, много проксиrequest
а такжеresponse
свойства и методы , переданные как глобальный объект - request.js
koa
к родномуreq
инкапсуляция объекта - response.js
koa
к родномуres
инкапсуляция объекта
request.js
а такжеresponse.js
Нечего сказать, любой веб-фреймворк предоставитreq
а такжеres
пакет для упрощения обработки. Так что смотрите в основномcontext.js
а такжеapplication.js
реализация
// context.js
/**
* Response delegation.
*/
delegate(proto, 'res')
.method('setHeader')
/**
* Request delegation.
*/
delegate(proto, 'req')
.access('url')
.setter('href')
.getter('ip');
context
Это своего рода код, основная функция которого заключается в том, чтобы выступать в роли прокси, использоватьdelegate
библиотека.
Кратко объясните здесь значение прокси, напримерdelegate(proto, 'res').method('setHeader')
Что делает это утверждение:Когда вызывается proto.setHeader, вызывается proto.res.setHeaderТо есть будетproto
изsetHeader
делегировать методproto
изres
Свойства, другие аналогичны.
// application.js 中部分代码
constructor() {
super()
this.middleware = []
this.context = Object.create(context)
}
use(fn) {
this.middleware.push(fn)
}
listen(...args) {
debug('listen')
const server = http.createServer(this.callback());
return server.listen(...args);
}
callback() {
// 这里即中间件处理代码
const fn = compose(this.middleware);
const handleRequest = (req, res) => {
// ctx 是koa的精髓之一, req, res上的很多方法代理到了ctx上, 基于 ctx 很多问题处理更加方便
const ctx = this.createContext(req, res);
return this.handleRequest(ctx, fn);
};
return handleRequest;
}
handleRequest(ctx, fnMiddleware) {
ctx.statusCode = 404;
const onerror = err => ctx.onerror(err);
const handleResponse = () => respond(ctx);
return fnMiddleware(ctx).then(handleResponse).catch(onerror);
}
то же самое вlisten
метод созданweb
обслуживание, не используетсяexpress
так наоборот,const server = http.createServer(this.callback());
использоватьthis.callback()
генерироватьweb
обработчик службы
callback
возврат функцииhandleRequest
, поэтому настоящий обработчикthis.handleRequest(ctx, fn)
Обработка промежуточного программного обеспечения
Конструкторconstructor
Поддерживать глобальный массив промежуточного ПО вthis.middleware
и глобальныйthis.context
Экземпляр (также в исходном коде есть объекты запроса, ответа и некоторые другие вспомогательные свойства). а такжеexpress
отличается, потому что нетrouter
реализация, всеthis.middleware
являются обычными функциями «промежуточного программного обеспечения», а не сложнымиlayer
пример,
this.handleRequest(ctx, fn);
серединаctx
первый параметр,fn = compose(this.middleware)
В качестве второго параметраhandleRequest
позвонюfnMiddleware(ctx).then(handleResponse).catch(onerror);
Таким образом, ключом к промежуточной обработке являетсяcompose
метод, который является автономным пакетомkoa-compose
, выньте его и посмотрите, что внутри:
// compose.js
'use strict'
module.exports = compose
function compose (middleware) {
return function (context, next) {
let index = -1
return dispatch(0)
function dispatch (i) {
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)
}
}
}
}
а такжеexpress
серединаnext
Он не похож, а он естьpromise
Формально из-за того, что он должен поддерживать асинхронность, понять его немного труднее: каждый中间件
Являетсяasync (ctx, next) => {}
, который возвращаетpromise
, второй параметрnext
ценностьdispatch.bind(null, i + 1)
, используется для переноса исполнения «мидлваров», один за другим мидлвары выполняются внутрь, пока не будет выполнен последний промежуточный код,resolve
удаляется, его предыдущее «промежуточное ПО» затем выполняетсяawait next()
код после, затемresolve
Бросьте, продолжайте двигаться вперед до первого «мидлвара»resolve
выключено, в конечном итоге делая крайнийpromise
resolve
Терять.
здесь и
express
очень отличается, чтоkoa
Обработка ответа не в "промежуточном программном обеспечении", а возвращается после выполнения промежуточного программного обеспечения.promise
resolve
назад:
return fnMiddleware(ctx).then(handleResponse).catch(onerror);
пройти через
handleResponse
Наконец, ответ обрабатывается, и «промежуточное ПО» будет установлено.ctx.body
,handleResponse
также будет в основном заниматьсяctx.body
,такkoa
Будет установлена модель «луковое кольцо»,await next()
Следующий код также влияет на окончательный ответ.
На этом промежуточная реализация koa завершена.
redux
должен сказать,redux
Идея дизайна и реализация исходного кода действительно прекрасны, общий объем кода невелик, и его можно увидеть повсюду в Интернете.redux
анализ исходного кода, не буду вдаваться в подробности. Тем не менее, я все же рекомендую волну описания промежуточного программного обеспечения на официальном сайте:redux-middleware
Это лучшая документация, которую я когда-либо читал, без исключения, она ясно объясняетredux middleware
Процесс эволюции прекрасно выводит分析问题
прибыть解决问题
, и постоянно оптимизировать мыслительный процесс.
в общем и целом
В этой статье по-прежнему в основном рассматривается его реализация промежуточного программного обеспечения, сначала кратко поговорим о нем.redux
Основная логика обработкиcreateStoreэто его программа входа, фабричный метод, возвращающийstore
пример,store
Самый критический метод экземпляраdispatch, а такжеdispatch
Все, что нужно, это одно:
currentState = currentReducer(currentState, action)
то есть позвонитьreducer
, пройти в текущемstate
а такжеaction
вернуть новыйstate
.
Таким образом, чтобы смоделировать основныеredux
реализовать толькоcreateStore
, dispatch
метод. другой контент, напримерbindActionCreators
, combineReducers
так же какsubscribe
Мониторинг является вспомогательной функцией и может временно игнорироваться.
Обработка промежуточного программного обеспечения
Затем идет основная часть реализации «промежуточного программного обеспечения», а именноapplyMiddleware.js:
// applyMiddleware.js
import compose from './compose'
export default function applyMiddleware(...middlewares) {
return createStore => (...args) => {
const store = createStore(...args)
let dispatch = () => {
throw new Error(
`Dispatching while constructing your middleware is not allowed. ` +
`Other middleware would not be applied to this dispatch.`
)
}
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
const chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
redux
Расширения, предоставляемые промежуточным программным обеспечением, находятся вaction
После начала, прибывreducer
Раньше идея его реализации была такой же, какexpress
,koa
Что-то другое, не проходит инкапсуляцияstore.dispatch
, добавьте перед ним中间件处理程序
, но путем рекурсивной перезаписиdispatch
, постоянно передавая последний переопределенныйdispatch
реализовать.
Каждыйredux
Промежуточное ПО представлено в видеstore => next => action => { xxx }
Здесь в основном два уровня вложенности функций:
-
Самая внешняя функция получает параметры
store
, соответствующийapplyMiddleware.js
Код обработки вconst chain = middlewares.map(middleware => middleware(middlewareAPI))
,middlewareAPI
входящийstore
. Этот слой долженstore
изapi
Передано промежуточному программному обеспечению для использования, в основном дваapi
:-
getState
, пройти напрямуюstore.getState
. -
dispatch: (...args) => dispatch(...args)
,Реализация здесь очень умная, неstore.dispatch
, а внешняя переменнаяdispatch
, эта переменная, наконец, указывает на перезаписанныйdispatch
, причина этого в том, что дляredux-thunk
Такое асинхронное промежуточное ПО, внутренний вызовstore.dispatch
Когда еще после прохождения всего "мидлвара".
-
-
вернуть
chain
Это массив второго слоя, и каждый элемент массива является такой функциейnext => action => { xxx }
, эту функцию можно понимать как接受一个dispatch
вернутьdispatch
, принятыйdispatch
возвращается последним промежуточным ПОdispatch
. -
Еще одной ключевой функцией являетсяcompose, основная функция
compose(f, g, h)
вернуть() => f(g(h(..args)))
теперь понятьdispatch = compose(...chain)(store.dispatch)
относительно простой, роднойstore.dispatch
Передайте последнее «мидлваре», верните новыйdispatch
, а затем передается предыдущему промежуточному ПО до окончательногоdispatch
, когда перезаписываетсяdispatch
При вызове выполнение каждого «промежуточного программного обеспечения» представляет собой модель «лукового кольца» снаружи внутрь.
На этом промежуточное ПО redux завершено.
Другие ключевые моменты
redux
Есть еще один момент в реализации промежуточного программного обеспечения, который стоит изучить: чтобы «промежуточное программное обеспечение» применялось только один раз,applyMiddleware
не действует наstore
пример, но действуйтеcreateStore
заводской метод. Как понять это? еслиapplyMiddleware
такое, что
(store, middlewares) => {}
затем, когда несколько вызововapplyMiddleware(store, middlewares)
Одно и то же промежуточное ПО будет неоднократно добавляться к одному и тому же экземпляру. такapplyMiddleware
в виде
(...middlewares) => (createStore) => createStore
,
Таким образом, каждый раз при применении ПО промежуточного слоя создается новый экземпляр, что позволяет избежать проблемы повторного применения ПО промежуточного слоя.
Эта форма получитmiddlewares
вернутьcreateStore
Метод высшего порядка , этот метод обычно называютcreateStore
изenhance
метод, приложение промежуточного программного обеспечения добавляется внутри, вы найдете этот метод и второй уровень промежуточного программного обеспечения(dispatch) => dispatch
согласуется с формой, поэтому его также можно использовать дляcompose
Сделайте несколько улучшений. в то же времяcreateStore
Есть еще третий параметрenhance
Для внутреннего суждения, самоутверждения. такredux
Использование промежуточного программного обеспечения может быть записано двумя способами:
第一种:用 applyMiddleware 返回 enhance 增强 createStore
store = applyMiddleware(middleware1, middleware2)(createStore)(reducer, initState)
第二种: createStore 接收一个 enhancer 参数用于自增强
store = createStore(reducer, initState, applyMiddleware(middleware1, middleware2))
Второе использование будет более интуитивным и более читабельным.
На протяженииredux
Реализация функционального программирования ярко отражена в форме промежуточного программного обеспечения.store => next => action => { xx }
Это гибкое воплощение каррирования функций, которое преобразует несколько параметров в одиночные параметры, которые можно использовать для предварительной фиксации.store
параметры, чтобы получить более явную формуdispatch => dispatch
, так чтоcompose
быть в состоянии функционировать.
Суммировать
В общем и целом,express
а такжеkoa
Реализация очень похожа, обеnext
Передача методов делает рекурсивные вызовы, ноkoa
даpromise
форма.redux
По сравнению с двумя предыдущими, он немного отличается.Во-первых, он перезаписывается рекурсией для формирования рекурсивного внутреннего вызова во время выполнения.
Суммируйте ключевые сходства и различия трех (не ограничиваясь промежуточным ПО):
- Создание экземпляра:
express
Используя фабричный метод,koa
это класс -
koa
Реализованный синтаксис более продвинутый, с использованиемES6
,служба поддержкиgenerator(async await)
-
koa
нет встроенногоrouter
, повысилсяctx
Глобальный объект, общий код более лаконичен и удобен в использовании. -
koa
Рекурсия промежуточного программного обеспеченияpromise
форма,express
использоватьwhile
петля плюсnext
хвостовая рекурсия - я предпочитаю
redux
Реализация промежуточного программного обеспечения каррирования более краткая и гибкая, а функциональное программирование более очевидное. -
redux
кdispatch
Улучшение промежуточного ПО путем переопределения
Наконец-то снова прикрепилИсходный код примера моделированияДля справки по обучению, если вам это нравится, добро пожаловать в звездочку и форк!
ответь на вопрос
кто-то сказал,express
также можно использовать вasync function
Как промежуточное ПО для асинхронной обработки?На самом деле невозможно, т.к.express
Выполнение промежуточного программного обеспечения является синхроннымwhile
цикл, когда промежуточное ПО также содержит普通函数
а такжеasync 函数
, порядок выполнения будет нарушен, сначала посмотрите на этот пример:
function a() {
console.log('a')
}
async function b() {
console.log('b')
await 1
console.log('c')
await 2
console.log('d')
}
function f() {
a()
b()
console.log('f')
}
Выход здесь'a' > 'b' > 'f' > 'c'
Вызовите его непосредственно в обычной функцииasync
функция,async
Функция будет выполняться синхронно до первогоawait
код после, а затем немедленно возвращаетpromise
, подождите, пока все внутренниеawait
Асинхронное завершение, весьasync
После выполнения функцииpromise
будуresolve
Терять.
Таким образом, с помощью приведенного выше анализаexpress
Реализация промежуточного программного обеспечения, если используетсяasync
Функция промежуточного программного обеспечения, используемая внутриawait
Выполните асинхронную обработку, тогда следующее промежуточное ПО будет выполнено первым и подождет, покаawait
перезвонить послеnext
Индекс будет превышен! , вы можете все сами здесьexpress asyncОткройте аннотации и попробуйте сами.
10.03.2020 Исправление ошибки
Упрощенный макет экспресса неверен
while(idx < stack.length) {
mid = stack[idx++];
mid(req, res, next);
}
был исправлен на
mid = stack[idx++];
mid(req, res, next);
Таким образом, экспериментальный результат последнего вопроса также неверен.Вывод состоит в том, что промежуточное программное обеспечение экспресса может использовать асинхронность, если оно не используется в качестве луковых колец.