Egg наследует Koa, Egg выбирает Koa в качестве базовой структуры и дополнительно совершенствует ее на основе своей модели.
Выполнение анализа процесса
Промежуточное ПО Koa выбирает модель лукового кольца.
Все запросы будут выполняться дважды, когда они проходят через промежуточное программное обеспечение.По сравнению с промежуточным программным обеспечением Express, модель Koa может легко реализовать логику постобработки.Вы можете видеть, что выполнение выполняется спереди назад, а затем сзади вперед.
Диаграмма последовательности выполнения промежуточного программного обеспечения
Анализ структуры каталогов
egg-project
├── package.json
├── app.js (可选)
├── agent.js (可选)
├── app
| ├── router.js
│ ├── controller
│ | └── home.js
│ ├── service (可选)
│ | └── user.js
│ ├── middleware (可选)
│ | └── response_time.js
│ ├── schedule (可选)
│ | └── my_task.js
│ ├── public (可选)
│ | └── reset.css
│ ├── view (可选)
│ | └── home.tpl
│ └── extend (可选)
│ ├── helper.js (可选)
│ ├── request.js (可选)
│ ├── response.js (可选)
│ ├── context.js (可选)
│ ├── application.js (可选)
│ └── agent.js (可选)
├── config
| ├── plugin.js
| ├── config.default.js
│ ├── config.prod.js
| ├── config.test.js (可选)
| ├── config.local.js (可选)
| └── config.unittest.js (可选)
└── test
├── middleware
| └── response_time.test.js
└── controller
└── home.test.js
Каталоги, согласованные в рамках:
-
app/router.js
Используется для настройки правил маршрутизации URL -
app/controller/**
Используется для анализа ввода пользователя и возврата соответствующего результата после обработки. -
app/service/**
Используется для написания уровня бизнес-логики, необязательно -
app/middleware/**
для написания промежуточного ПО, опционально -
app/public/**
Используется для размещения статических ресурсов, опционально -
app/extend/**
Расширения для фреймворка, опционально -
config/config.{env}.js
Для записи файлов конфигурации -
config/plugin.js
Используется для настройки подключаемых модулей для загрузки. -
test/**
для модульного тестирования -
app.js
а такжеagent.js
Используется для настройки работы инициализации при запуске, опционально
Каталоги, согласованные встроенными плагинами:
-
app/public/**
Используется для размещения статических ресурсов, опционально -
app/schedule/**
для запланированных задач
порядок загрузки файлов
Egg относится к приложениям, фреймворкам и плагинам как loadUnits.
документ | заявление | Рамка | плагин |
---|---|---|---|
package.json | ✔︎ | ✔ | ︎ ✔ |
config/plugin.{env}.js | ✔︎ | ✔︎ | |
config/config.{env}.js | ✔︎ | ✔︎ | ✔︎ |
app/extend/application.js | ✔︎ | ✔ | ︎ ✔︎ |
app/extend/request.js | ✔︎ | ✔︎ | ✔︎ |
app/extend/response.js | ✔︎ | ✔︎ | ✔︎ |
app/extend/context.js | ✔︎ | ✔︎ | ✔︎ |
app/extend/helper.js | ✔︎ | ✔︎ | ✔︎ |
agent.js | ✔︎ | ✔︎ | ✔︎ |
app.js | ✔︎ | ✔︎ | ✔︎ |
app/service | ✔︎ | ✔︎ | ✔︎ |
app/middleware | ✔︎ | ✔︎ | ✔︎ |
app/controller | ✔︎ | ||
app/router.js | ✔︎ |
Файлы загружаются сверху вниз в порядке в таблице, Egg будет проходить через все loadUnits, чтобы загрузить вышеуказанные файлы (приложения, фреймворки и плагины разные), и при загрузке есть определенный приоритет.
- Загружается последовательно плагином => фреймворк => приложение
- Порядок между плагинами определяется отношениями зависимости, зависимая сторона загружается первой, и никакие зависимости не загружаются в соответствии с порядком конфигурации ключа объекта.
- Фреймворки загружаются в порядке наследования, чем ниже уровень, тем первым
Жизненный цикл
- Файл конфигурации вот-вот будет загружен, это последний раз, когда нужно динамически изменить конфигурацию (
configWillLoad
) - Файл конфигурации загружается (
configDidLoad
) - загрузка файла завершена (
didLoad
) - Плагин запущен(
willReady
) - Рабочий готов(
didRead
) - Запуск приложения завершен (
serverDidReady
) - Приложение вот-вот закроется (
beforeClose
)
Определяется следующим образом:
// app.js or agent.js
class AppBootHook {
constructor(app) {
this.app = app;
}
configWillLoad() {
// Ready to call configDidLoad,
// Config, plugin files are referred,
// this is the last chance to modify the config.
console.log('configWillLoad');
}
configDidLoad() {
// Config, plugin files have been loaded.
console.log('configDidLoad');
}
async didLoad() {
// All files have loaded, start plugin here.
console.log('didLoad');
}
async willReady() {
// All plugins have started, can do some thing before app ready
console.log('willReady');
}
async didReady() {
// Worker is ready, can do some things
// don't need to block the app boot.
console.log('didReady');
}
async serverDidReady() {
// Server is listening.
console.log('serverDidReady');
}
async beforeClose() {
// Do some thing before app close.
console.log('configWillLoad');
}
}
module.exports = AppBootHook;
Последовательность выполнения при запуске
Встроенные базовые объекты фреймворка*
Некоторые базовые объекты встроены в фреймворк, в том числе 4, унаследованные от Koa.
- Application
- Context
- Request
- Response
и некоторые объекты, которые расширяет фреймворк
- Controller
- Service
- Helper
- Config
- Logger
Application
Приложение — это глобальный объект приложения. В приложении будет создан только один экземпляр. Он наследуется отKoa.Application, на который мы можем смонтировать некоторые глобальные методы и объекты.
мероприятие
-
server
Это событие будет инициировано рабочим процессом только один раз.После запуска службы HTTP сервер HTTP будет доступен разработчику через это событие. -
error
После того, как плагин onerror перехватит любое исключение во время выполнения, будет запущено событие ошибки. -
request
а такжеresponse
Когда приложение получает запрос и отвечает на запрос, оно инициирует события запроса и ответа соответственно.
// app.js
module.exports = app => {
app.once('server', server => {
// websocket
console.log('server', server);
});
app.on('error', (err, ctx) => {
// report error
console.log('error', err);
});
app.on('request', ctx => {
// log receive request
console.log('request');
});
app.on('response', ctx => {
// ctx.starttime is set by framework
const used = Date.now() - ctx.starttime;
// log total cost
console.log('used', used);
});
};
// 初始化的时候会打印 server Server
// request
// used 6
// request
// used 1
способ получения
Почти все файлы, загружаемые загрузчиком фреймворка (контроллер, служба, расписание и т. д.), могут экспортировать функцию, которая будет вызываться загрузчиком с приложением в качестве параметра.
- Запустить пользовательский скрипт
// app.js
module.exports = app => {
app.cache = new Cache();
};
- Файл контроллера
// app/controller/user.js
class UserController extends Controller {
async fetch() {
this.ctx.body = this.app.cache.get(this.ctx.query.id);
}
}
Как и Koa, в объекте Context доступ к объекту Application можно получить через ctx.app. то есть
результат верен// app/controller/user.js
class UserController extends Controller {
async fetch() {
this.ctx.body = this.ctx.app.cache.get(this.ctx.query.id);
}
}
Context
Контекст — это объект уровня запроса, унаследованный отKoa.Context
Каждый раз, когда поступает пользовательский запрос, платформа создает экземпляр объекта Context, который инкапсулирует информацию, запрошенную пользователем, и предоставляет множество удобных методов для получения параметров запроса или установки информации ответа.
способ получения
// Koa v1
function* middleware(next) {
// this is instance of Context
console.log(this.query);
yield next;
}
// Koa v2
async function middleware(ctx, next) {
// ctx is instance of Context
console.log(ctx.query);
}
В дополнение к получению экземпляра Context во время запроса, в некоторых сценариях, которые не запрошены пользователем, нам необходимо получить доступ к объектам экземпляра Context, таким как служба/модель, которые можно использоватьApplication.createAnonymousContext()
метод создает анонимный экземпляр Context.
// app.js
module.exports = app => {
app.beforeStart(async () => {
const ctx = app.createAnonymousContext();
// preload before app start
await ctx.service.posts.load();
});
}
Каждая задача в запланированной задаче принимает экземпляр Context в качестве параметра.
// app/schedule/refresh.js
exports.task = async ctx => {
await ctx.service.posts.refresh();
};
Request & Response
-
Request
объект уровня запроса, унаследованный отKoa.Request. Он инкапсулирует собственный объект HTTP-запроса Node.js и предоставляет ряд вспомогательных методов для получения общих параметров HTTP-запросов. -
Response
объект уровня запроса, унаследованный отKoa.Response. Он инкапсулирует собственный объект HTTP-ответа Node.js и предоставляет ряд вспомогательных методов для установки HTTP-ответа.
способ получения
Вы можете получить запрос текущего запроса (ctx.request
) и Ответ(ctx.response
) пример.
// app/controller/user.js
class UserController extends Controller {
async fetch() {
const { app, ctx } = this;
const id = ctx.request.query.id;
ctx.response.body = app.cache.get(id);
}
}
- Koa будет проксировать некоторые методы и свойства в запросе и ответе в контексте, см.Koa.Context
- как в примере выше
ctx.request.query.id
а такжеctx.query.id
эквивалентно,ctx.response.body=
а такжеctx.body=
эквивалентен - Следует отметить, что для получения тела POST следует использовать
ctx.request.body
, вместоctx.body
Controller
Платформа предоставляет базовый класс контроллера и рекомендует, чтобы все контроллеры наследуются от реализации этого базового класса.
Базовый класс Controller имеет следующие свойства:
-
ctx
- экземпляр контекста текущего запроса -
app
- Экземпляр приложения -
config
- Конфигурация приложения -
service
- применять все услуги -
logger
- объект логгера, инкапсулированный для текущего контроллера
В файле Controller на базовый класс Controller можно ссылаться двумя способами:
// app/controller/user.js
// 从 egg 上获取(推荐)
const Controller = require('egg').Controller;
class UserController extends Controller {
// implement
}
module.exports = UserController;
// 从 app 实例上获取
module.exports = app => {
return class UserController extends app.Controller {
// implement
};
};
Service
Платформа предоставляет базовый класс службы и рекомендует, чтобы все службы наследуются от реализации этого базового класса.
Свойства базового класса Service такие же, как у базового класса Controller, и методы доступа аналогичны:
// app/service/user.js
// 从 egg 上获取(推荐)
const Service = require('egg').Service;
class UserService extends Service {
// implement
}
module.exports = UserService;
// 从 app 实例上获取
module.exports = app => {
return class UserService extends app.Service {
// implement
};
};
Helper
Helper используется для предоставления некоторых полезных служебных функций. Его функция заключается в том, что мы можем выделить некоторые часто используемые действия в независимую функцию в helper.js, чтобы мы могли использовать JavaScript для написания сложной логики, избежать разбросанной повсюду логики и лучше писать тестовые примеры.
Сам помощник класса, и имейте одинаковые атрибуты контроллера базового класса, он будет создан для каждого запроса, все функции на хелпере могут получить текущую информацию контекста, связанную с запросом.
способ получения
Вы можете получить текущий запрошенный помощник в экземпляре Context (ctx.helper
) пример.
// app/controller/user.js
class UserController extends Controller {
async fetch() {
const { app, ctx } = this;
const id = ctx.query.id;
const user = app.cache.get(id);
ctx.body = ctx.helper.formatUser(user);
}
}
собственный вспомогательный метод
// app/extend/helper.js
module.exports = {
formatUser(user) {
return only(user, [ 'name', 'phone' ]);
}
};
Config
Рекомендуется, чтобы разработка приложений следовала принципу разделения конфигурации и кода и помещала некоторые бизнес-конфигурации, которые необходимо жестко закодировать в файл конфигурации.В то же время файл конфигурации поддерживает различные операционные среды для использования разных конфигураций, которые также очень удобен в использовании.Все фреймворки и плагины И конфигурация уровня приложения могут быть получены через объект Config.
способ получения
пройти черезapp.config
Получите объект конфигурации из экземпляра приложения или передайте его экземплярам контроллера, службы, помощника.this.config
Получите объект конфигурации.
Logger
Каждый объект регистратора предоставляет четыре уровня методов:
logger.debug()
logger.info()
logger.warn()
logger.error()
способ получения
-
app.logger
Если мы хотим вести журнал на уровне приложения, например, записывать некоторую информацию о данных на этапе запуска и записывать некоторую независимую от бизнеса информацию, мы можем сделать это с помощью App Logger. -
app.coreLogger
При разработке приложений не следует использовать CoreLogger для печати журналов, но фреймворки и плагины должны использовать его для печати журналов на уровне приложения, чтобы можно было более четко различать журналы, печатаемые приложением и фреймворком. by CoreLogger будет помещен в а Logger в разные файлы -
ctx.logger
Он получен из экземпляра Context.Из метода доступа мы видим, что Context Logger должен быть связан с запросом, а журналы, которые он печатает, принесут некоторую информацию, связанную с текущим запросом впереди (например,[$userId/$ip/$traceId/${cost}ms $method $url]
) -
this.logger
Может передавать экземпляры контроллера и службыthis.logger
Получите их, по сути, это Context Logger, но при печати журнала будет добавлен дополнительный путь к файлу.