Анализ исходного кода Egg.js — старт проекта

JavaScript Egg.js koa

объявление о работе

Дамы и господа, резюме можно отправлять на адрес ivanxjfan@tencent.com.

Пожалуйста, укажите имя резюме в формате: Имя-Должность-Рабочие годы-Место работы (например: Чжан Сан-Внешняя разработка-Пять лет-Чанша.pdf)


Компания:Тенсент

Место:Чанша

Позиция:разработчик веб-интерфейса

Рабочие обязанности:

Отвечает за системные исследования и разработку продуктов Tencent Cloud DNSPod, полные интерфейсные функции системы и реализацию внутреннего логического кода, а также обеспечивает качество продукта и прогресс в исследованиях и разработках.

профессиональные требования:

1. Степень бакалавра или выше, специальность компьютерная, опыт работы более 2 лет;

2. Владение Javascript, html, css и другими технологиями фронтенд-разработки с прочной базой;

3. Знакомство с текущими основными интерфейсными фреймворками (react/vue и т. д.), приветствуется опыт разработки на React и Redux;

4. Знакомы с протоколами HTTP и TCP/IP, хорошо разбираетесь в вопросах безопасности и знакомы с общими стратегиями атак и защиты в сети;

5. Хорошие аналитические способности и навыки решения проблем, а также энтузиазм в обучении;

6. Приветствуется опыт разработки Node.js/PHP;

7. Предпочтение отдается разработчикам плагинов с опытом работы в WP или DZ

Примечание. Этот пост подготовлен дочерней компанией Tencent Group. сообщение"


Компания:Тенсент

Место:Штаб-квартира Tencent в Шэньчжэне

Позиция:Старший разработчик веб-интерфейса

Рабочие обязанности:

Отвечает за проектирование системной архитектуры и исследования и разработки продуктов Tencent Cloud Domain Name (DNSPod).

профессиональные требования:

1. Степень бакалавра или выше, специальность компьютерная, опыт работы не менее 5 лет;

2. Владение Javascript, html, css и другими технологиями фронтенд-разработки с прочной базой;

3. Знакомство с текущими основными интерфейсными фреймворками (react/vue и т. д.), приветствуется опыт разработки на React и Redux;

4. Знакомы с протоколами HTTP и TCP/IP, хорошо разбираетесь в вопросах безопасности и знакомы с общими стратегиями атак и защиты в области сетевой безопасности;

5. Хорошие аналитические способности и навыки решения проблем, а также энтузиазм в обучении;

6. Приветствуется опыт разработки Node.js/PHP;

предисловие

Внешнее время требует времени дляKoa2Исходный код прост в освоении,koaИсходный код представляет собой очень простую библиотеку, для процесса анализа я хочу реализовать тип вручную.koaкаркас, которыйкод, реализуйте простую версию Koa в соответствии с пошаговым улучшением, каждый шаг одинBranch, Такие как:stpe-1, соответствует коду, который я хочу реализовать на первом этапе. Код предназначен только для моего собственного простого обучения. Многие места не идеальны. Я просто хочу испытать идею Коа. Следующие пункты являются моим простым пониманием Коа:

  • Самое основное ядро ​​всех фреймворков NodeJS — нативные библиотеки.http or httpsЗапустить серверную службуhttp.createServer(this.serverCallBack()).listen(...arg), то все запросы будут попадать вserverCallBackметод, и тогда мы можем обрабатывать различные запросы в этом методе, перехватывая
  • Koa — это onion-модель, реализованная на основе промежуточного программного обеспечения.useдобавить промежуточное ПО,koa-routerНа самом делеkoaПромежуточное ПО, все наши запросы будут выполнять все промежуточное ПО, луковая модель показана на следующем рисунке.

Вышеизложенное является моим простым пониманием анализа исходного кода Koa, и я позже запишу свое понимание Koa. Koa — это очень маленькая и гибкая платформа. В отличие от Express, в Express интегрировано множество функций. Многие функции больше не требуют сторонних платформ, таких как функции маршрутизации. Koa необходимо обратиться к сторонней библиотеке koa-router для реализации маршрутизации, и т.п. Но для экспресса это не нужно.Вот Koa и Express, две демки, которые реализуют простую функцию.Можем сравнить их использование:

// Express
const express = require('express')
const app = express()

app.get('/', function (req, res) {
  res.send('Hello World!')
})

app.listen(3000, function () {
  console.log('Example app listening on port 3000!')
})
// Koa 
var Koa = require('koa');
// 引用第三方路由库
var Router = require('koa-router');

var app = new Koa();
var router = new Router();
router.get('/', (ctx, next) => {
  // ctx.router available
});
// 应用中间件: router
app
  .use(router.routes())
  .use(router.allowedMethods());
app.listen(3000);

Ха-ха, выше мы наговорили много ерунды (проблема писательских способностей), на самом деле я хочу проанализировать, как это применить на основе фреймворка Koa,eggjsЭто фреймворк, основанный на фреймворке Koa, подробно разберем его ниже.eggjsРамка.

Основное использование Eggjs

Мы основаны наБыстрый старт, Вы можете быстро построить структуру проекта Egg,

$ npm i egg-init -g
$ egg-init egg-example --type=simple
$ cd egg-example
$ npm i

мы можем использоватьnpm run devБыстрый старт проекта. Затем откройтеlocalhost:7001Вы можете увидеть вывод страницы:

hi, egg.

Это означает, что инициализация нашего проекта завершена и он успешно запущен. Теперь мы можем изучить соответствующий код, созданный проектом яйца. Его структура файла кода выглядит следующим образом:

Проанализировал всю файловую структуру и не нашел входных файлов, таких как app.js, во всем проекте (я обычно начинаю с входного файла при изучении нового фреймворка), нашелappПод папкой находится код, который должен быть важен для проекта:

1. Папка контроллера, как мы это понимаем, должна быть файлом слоя управления, в котором есть код home.js следующего вида:

'use strict';

const Controller = require('egg').Controller;

class HomeController extends Controller {
  async index() {
    this.ctx.body = 'hi, egg';
  }
}

module.exports = HomeController;

Этот класс наследует класс контроллера яйца, и я не нашел в этом проекте места, где бы это упоминалось.ControllerДобрый?

2, аrouter.jsфайл, мы можем буквально понять его как файл маршрутизации, и его код выглядит следующим образом:

'use strict';

/**
 * @param {Egg.Application} app - egg application
 */
module.exports = app => {
  const { router, controller } = app;
  router.get('/', controller.home.index);
};

В этом файле представлен метод, который с этого момента должен быть некоторой конфигурацией маршрутизации, но я не нашел его во всем проекте. Где ссылка на этот метод?router.get('/', controller.home.index);, но из второго параметра этого метода get, похоже, он указывает на метод index файла home.js в контроллере, мы можем попробовать изменить файл home.jsthis.ctx.body = 'hi, egg -> hello world!';, затем перезапуститеnpm run dev, обнаружите, что вывод страницыhi, egg -> hello world!, похоже на тоcontroller.home.indexБез сомнения, это указывает на метод index в home.js, ноcontroller.home.indexМетод индекса связан вcontrollerНа объекте, когда он связан?

Далее мы изучим некоторые вопросы со следующими вопросамиeggjs :

  • Без аналогичного файла записи app.js запуститеnpm run devКак запустить проект (запустить сервер, прослушать порт, добавить промежуточное ПО)?
  • мы открываем страницуhttp://localhost:7001/, Как найти маршрут через router.js, а потом вызвать соответствующую callback-функцию?
  • Как контроллер привязан к объекту контроллера в приложении?

запуск яйца

Давайте сначала посмотрим наegg-initФайл package.json проекта, созданный командой, посмотретьscripts, который содержит ряд команд, как показано ниже:

Мы можемnpm run startдля запуска программы, но есть командаdebug, мы можем пройтиnpm run debugкоманда для отладки программы eggjs, соответствующая командаegg-bin debug, так что вся наша запись и есть эта команда, разберем ее подробно нижеegg-bin debugкак это работает.

egg-bin

в ящике для яицstart-clusterДокумент, вызывающий метод ввода EGGJS:require(options.framework).startCluster(options);где options.framework указывает на абсолютный путьD:\private\your_project_name\node_modules\egg(то есть,eggмодуль), выполнить напрямуюD:\private\your_project_name\node_modules\egg\index.jsнезащищенныйexports.startCluster = require('egg-cluster').startCluster;изstartClusterметод. Ниже мы проанализируемegg-clusterмодуль.

egg-cluster

Структура проекта egg-cluster состоит из двух основных файлов:master.js, app_worker.jsдва файла,

master.jsЭто связано с многопоточностью nodejs, давайте сначала пропустим это и изучим непосредственноapp_worker.jsфайл, чтобы изучить процесс запуска eggjs. Ниже приведены основные шаги, выполняемые app_worker.js.

  1. const Application = require(options.framework).Application;, импортировать модуль eggjs, optons.framework указывает наD:\private\your_project_name\node_modules\egg
  2. const app = new Application(options);(создать экземпляр яйца)
  3. app.ready(startServer);Вызовите метод ** ready ** объекта яйца, чей startServer является функцией обратного вызова, функция которой состоит в вызове собственного модуля nodejs.http or httpsизcreateServerСоздайте службу nodejs (server = require('http').createServer(app.callback());, мы подробно проанализируем этот метод позже).

На трех предыдущих шагах была запущена служба nodejs и отслеживается порт. То есть наш первый вопрос решен:

Нет аналогичного файла записи app.js, как запустить npm run dev для запуска проекта (запустить сервер, прослушивать порт, добавить промежуточное ПО)?

На самом деле, мы проанализировали только базовый процесс запуска eggjs и не задействовали основные библиотеки функций eggjs, а именно **egg** и **egg-core**, но мы уже создали один экземпляр выше. яйцаconst app = new Application(options);, давайте проанализируем основной модуль eggjs из этого входного файла.

egg & egg-core

В модулях egg и egg-core есть несколько основных классов, а именно:

Application(egg/lib/applicaton.js) -----> EggApplication(egg/lib/egg.js) -----> EggCore(egg-core/lib/egg.js) -----> KoaApplication(koa)

Из приведенного выше отношения, eggjs основан наkoaОн расширен на основе , поэтому начинаем анализ с конструктора базового класса (поскольку новое Приложение начнет выполнение с конструктора класса-наследника).

EggCore(egg-core/lib/egg.js)

Упростим конструктор, код такой

Как видно из рисунка выше, конструктор должен инициализировать многие базовые свойства, два из которых очень важны:

  1. this.lifecycleОтвечает за жизненный цикл всего экземпляра eggjs, позже мы подробно проанализируем весь жизненный цикл.
  1. this.loader(egg-core/lib/loader/egg_loader.js) решает, почему eggjs автоматически загружается после запуска службы, а путь к проектуrouter.js, controller/**.js, так же какservice/**.jsсвязываются сappНапример, далее мы сосредоточимся на анализе этого загрузчика.

EggApplication(egg/lib/egg.js)

Упростим конструктор, код такой

Этот конструктор также инициализирует многие базовые свойства, но есть вызовыEggCoreконструктор инициализированloaderизloadConfig()метод, этот метод, как следует из названия, заключается в загрузке конфигурации, которая указывает на:egg/lib/loader/app_worker_loader .jsМетодыloadConfig, этот метод заключается в следующем:

  loadConfig() {
    this.loadPlugin();
    super.loadConfig();
  }

Он загрузит все плагины, а затем загрузит все конфигурации.

this.loadPlugin() указывает наegg-core/lib/loader/mixin/plgin.jsМетодыloadPlugin, который загружает три плагина:

  • const appPlugins = this.readPluginConfigs(path.join(this.options.baseDir, 'config/plugin.default'));, плагин конфигурации приложения, т.е.your-project-name/config/plugin.js, то есть специальный плагин, который нужно настраивать под каждое приложение
  • const eggPluginConfigPaths = this.eggPaths.map(eggPath => path.join(eggPath, 'config/plugin.default'));, то есть настроенный из фреймворка eggjs плагин, путь которого находится вegg/config/plugin.js, то есть плагин, идущий в комплекте с фреймворком
  • process.env.EGG_PLUGINSВ-третьих, стартовый проект есть, в командной строке есть параметрыEGG_PLUGINSПлагин не должен широко использоваться.

Наконец, повесьте все плагины на экземпляр приложения.this.plugins = enablePlugins;,. (Мы узнаем, как работают эти плагины в будущем.)

будет выполняться следующимsuper.loadConfig()метод, который указывает наegg-core/lib/loader/mixin/config.jsизloadConfig()метод, Он также загружает четыре конфигурации:

  • const appConfig = this._preloadAppConfig();, конфиг конфигурации приложения, то есть особая конфигурация каждого приложения, которая будет загружать две конфигурации:
  const names = [
     'config.default',
     `config.${this.serverEnv}`,
   ];

Первый обязательно загрузит соответствующийconfig.defaultконфигурация, то естьyour-project-name/config/config.default.js, будет загружена конфигурация, не связанная с операционной средой, а затем будет загружена конфигурация, связанная с операционной средой, например:config.prod.js, config.test.js, config.local.js, config.unittest.js

  • загрузит все каталоги плагинов
   if (this.orderPlugins) {
     for (const plugin of this.orderPlugins) {
       dirs.push({
         path: plugin.path,
         type: 'plugin',
       });
     }
   }
  • Загрузит каталог проекта egg, который является каталогом egg/config.
    for (const eggPath of this.eggPaths) {
     dirs.push({
       path: eggPath,
       type: 'framework',
     });
   }
  • Вернитесь в каталог, в который был загружен проект приложения, т.your-project-name/config

Наконец, смонтируйте объединенную конфигурацию в экземпляре приложения.this.config = target;

мы можем открытьegg/config/config.default.jsфайл, вы можете проверить конфигурацию по умолчанию, одна из которых выглядит следующим образом:

  config.cluster = {
    listen: {
      path: '',
      port: 7001,
      hostname: '',
    },
  };

Очевидно, это должна быть конфигурация для запуска сервера, мы пока можем догадаться.

Мы проанализировали вышеegg-cluster/lib/app_worker.js, мы инициализируемappПосле того, как мы позвонимapp.ready(startServer);метод, мы можем предположитьstartServerМетод — это место, где запускается сервер nodejs.

существуетstartServerВ методе инициализируется http-серверserver = require('http').createServer(app.callback());, то даем слушатьserver.listen(...args);;, это сервер узла js запущен, можем проверить, могу проверить параметры args:

  const args = [ port ];
      if (listenConfig.hostname) args.push(listenConfig.hostname);
      debug('listen options %s', args);
      server.listen(...args);

Здесь параметр prot port добавляется к args, и мы можем перейти к месту, где определен prot:

const app = new Application(options);
const clusterConfig = app.config.cluster || /* istanbul ignore next */ {};
const listenConfig = clusterConfig.listen || /* istanbul ignore next */ {};
const port = options.port = options.port || listenConfig.port;

Мы видим, что порт в конечном итоге происходит от:app.config.cluster.listen.port, Отсюда мы знаем, как используется конфиг eggjs.

вопрос:

Если мы не хотим запускать проект eggjs, открытый порт по умолчанию не7001, как мы его изменим?

У нас должны быть следующие два пути:

  1. При выполнении команды npm run debug добавить соответствующие параметры
  2. Мы можем добавить конфигурацию в config/config.default.js нашего проекта, чтобы перезаписать настройки по умолчанию, например:

module.exports = appInfo => {
  const config = exports = {};

  // use for cookie sign key, should change to your own and keep security
  config.keys = appInfo.name + '_1541735701381_1116';

  // add your config here
  config.middleware = [];
  config.cluster = {
    listen: {
      path: '',
      port: 7788,
      hostname: '',
    },
  };
  return config;
};

Как и выше, когда мы снова запускаем проект, открытый порт: 7788.

думать:

Мы уже знаем, что можем настроить его в конфиге, какие еще приложения у нас есть в конфиге?

Мы знаем, что в разных операционных средах будут загружены разные конфигурации, поэтому, если мы занимаемся разработкой, путь для вызова API такой:http://dev.api.com, но при подключении к Интернету путь к приложению, которое мы вызываем, следующий:http://prod.api.com, мы можемconfig.prod.jsСредняя конфигурацияapiURL:http://prod.api.com, существуетconfig.local.jsКонфигурация:apiURL:http://prod.api.com

И затем, когда мы вызываем API, мы передаемapp.apiURLмогу.

Application(egg/lib/application.js)

Application(egg/lib/applicaton.js) -----> EggApplication(egg/lib/egg.js) -----> EggCore(egg-core/lib/egg.js) -----> KoaApplication(koa)

Мы поместили два вышеупомянутых основных класса: EggApplication(egg/lib/egg.js) -----> EggCore(egg-core/lib/egg.js), теперь мы анализируем класс верхнего уровня: Application (egg /lib/application.js).

Начнем все же с конструктора, нашли очень важную строчку кодаthis.loader.load();Это указывает на:app_worker_loader.jsМетод загрузки (egg/lib/loader/app_worker_loader.js) реализован следующим образом:

  load() {
    // app > plugin > core
    this.loadApplicationExtend();
    this.loadRequestExtend();
    this.loadResponseExtend();
    this.loadContextExtend();
    this.loadHelperExtend();
    // app > plugin
    this.loadCustomApp();
    // app > plugin
    this.loadService();
    // app > plugin > core
    this.loadMiddleware();
    // app
    this.loadController();
    // app
    this.loadRouter(); // Dependent on controllers
  }

Из этого метода мы видим, что загружено большое количество конфигураций, и мы можем проанализировать их одну за другой:

this.loadApplicationExtend();

Этот метод загрузит множество методов расширения для приложения.Путь загрузки: app\extend\application.js, и соответствующий объект будет смонтирован в приложении приложения. (Информацию об использовании см. в egg-jsonp/app/extend/application.js или egg-session/app/extend/application.js)

this.loadResponseExtend(); this.loadResponseExtend(); this.loadContextExtend(); this.loadHelperExtend();,

а такжеthis.loadApplicationExtend();Метод загрузки тот же, но соответствующие имена: request.js, response.js, helper.js, context.js.

this.loadCustomApp();

Для настраиваемых приложений загружаемый файл — это app.js (имя_вашего_проекта/app.js) в рамках соответствующего проекта, а его конкретный код реализован следующим образом: (egg-core/lib/loader/mixin/custom.js)

  [LOAD_BOOT_HOOK](fileName) {
    this.timing.start(`Load ${fileName}.js`);
    for (const unit of this.getLoadUnits()) { 
      const bootFilePath = this.resolveModule(path.join(unit.path, fileName));
      if (!bootFilePath) {
        continue;
      }
      const bootHook = this.requireFile(bootFilePath);
      // bootHook 是加载的文件
      if (is.class(bootHook)) {
        // if is boot class, add to lifecycle
        this.lifecycle.addBootHook(bootHook);
      } else if (is.function(bootHook)) {
        // if is boot function, wrap to class
        // for compatibility
        this.lifecycle.addFunctionAsBootHook(bootHook);
      } else {
        this.options.logger.warn('[egg-loader] %s must exports a boot class', bootFilePath);
      }
    }
    // init boots
    this.lifecycle.init();
    this.timing.end(`Load ${fileName}.js`);
  },

Из приведенного выше видно, что **bootHook** соответствует загруженному файлу, из приведенного вышеif elseВидно, что app.js должен показыватьclassилиfunction, затем позвонитеthis.lifecycle.addFunctionAsBootHook(bootHook);, код выглядит следующим образом:

  addFunctionAsBootHook(hook) {
    assert(this[INIT] === false, 'do not add hook when lifecycle has been initialized');
    // app.js is export as a funciton
    // call this function in configDidLoad
    this[BOOT_HOOKS].push(class Hook {
      constructor(app) {
        this.app = app;
      }
      configDidLoad() {
        hook(this.app);
      }
    });
  }

Вставьте соответствующий хук в this.lifecycleBOOT_HOOKSмассив, и завернутый в класс, и вconfigDidLoadВызовите соответствующий хук. Затем вызовитеthis.lifecycle.init();Чтобы инициализировать жизненный цикл:

  init() {
    assert(this[INIT] === false, 'lifecycle have been init');
    this[INIT] = true;
    this[BOOTS] = this[BOOT_HOOKS].map(t => new t(this.app));
    this[REGISTER_BEFORE_CLOSE]();
  }

этоinitМетод делает три вещи:

  • Отметьте статус INIT жизненного цикла как: true
  • Создайте экземпляр класса, соответствующего BOOT_HOOKS, и сохраните его вBOOTSначальство
  • Вызовите метод REGISTER_BEFORE_CLOSE, который вызовет наш хукbeforeCloseметод.

this.loadCustomApp();Методы, как показано ниже:

  loadCustomApp() {
    this[LOAD_BOOT_HOOK]('app');
    this.lifecycle.triggerConfigWillLoad();
  },

Итак, выполните следующийthis.lifecycle.triggerConfigWillLoad();

  triggerConfigWillLoad() {
    for (const boot of this[BOOTS]) {
      if (boot.configWillLoad) {
        boot.configWillLoad();
      }
    }
    this.triggerConfigDidLoad();
  }

  triggerConfigDidLoad() {
    for (const boot of this[BOOTS]) {
      if (boot.configDidLoad) {
        boot.configDidLoad();
      }
    }
    this.triggerDidLoad();
  }

вboot.configDidLoad();Это хук, определенный нашим app.js, который обрабатывается в классе Hook:

class Hook {
      constructor(app) {
        this.app = app;
      }
      configDidLoad() {
        hook(this.app);
      }
    }

Затем свяжите app.js с eggjs.

this.loadService();

Найдите your_project_name/app/service/.js, затем смонтируйте имя файла как свойство вcontext** context, а затем назначьте этому свойству соответствующий файл js и выставленный метод, например, мы находимся по следующему пути:your_project_name/app/service/home.js, код выглядит следующим образом:

'use strict';

// app/service/home.js
const Service = require('egg').Service;

class HomeService extends Service {
  async find() {
    // const user = await this.ctx.db.query('select * from user where uid = ?', uid);
    const user = [
      {
        name: 'Ivan Fan',
        age: 18,
      },
    ];
    return user;
  }
}

module.exports = HomeService;

В другом месте мы можем пройти:this.ctx.service.home.find()Сервисный метод вызовы внутри метода, такого как контроллер вызова:

'use strict';
const Controller = require('egg').Controller;
class HomeController extends Controller {
  async index() {
    // this.ctx.body = 'hi, egg';
    this.ctx.body = await this.ctx.service.home.find();
  }
}
module.exports = HomeController;

this.loadMiddleware();

Этот метод используется для загрузки промежуточного программного обеспечения, позже мы проанализируем промежуточное программное обеспечение отдельно.

this.loadController();

Этот метод предназначен для загрузки контроллера, код выглядит следующим образом:

  loadController(opt) {
    this.timing.start('Load Controller');
    opt = Object.assign({
      caseStyle: 'lower',
      directory: path.join(this.options.baseDir, 'app/controller'),
      initializer: (obj, opt) => {
        // return class if it exports a function
        // ```js
        // module.exports = app => {
        //   return class HomeController extends app.Controller {};
        // }
        // ```
        if (is.function(obj) && !is.generatorFunction(obj) && !is.class(obj) && !is.asyncFunction(obj)) {
          obj = obj(this.app);
        }
        if (is.class(obj)) {
          obj.prototype.pathName = opt.pathName;
          obj.prototype.fullPath = opt.path;
          return wrapClass(obj);
        }
        if (is.object(obj)) {
          return wrapObject(obj, opt.path);
        }
        // support generatorFunction for forward compatbility
        if (is.generatorFunction(obj) || is.asyncFunction(obj)) {
          return wrapObject({ 'module.exports': obj }, opt.path)['module.exports'];
        }
        return obj;
      },
    }, opt);
    const controllerBase = opt.directory;

    this.loadToApp(controllerBase, 'controller', opt);
    this.options.logger.info('[egg:loader] Controller loaded: %s', controllerBase);
    this.timing.end('Load Controller');
  },

Его путь загрузки: файл js в приложении/контроллере. Затем смонтируйте имя соответствующего файла в app.controller, а затем вызовите метод, предоставляемый js под контроллером, следующим образом:

module.exports = app => {
  const { router, controller } = app;
  router.get('/', controller.home.index);
};

Вышеизложенное должно решить наш первоначальный вопрос три:

  • Как контроллер привязан к объекту контроллера в приложении?

this.loadRouter();

Этот метод, как следует из названия, предназначен для загрузки маршрутизатора, и код выглядит следующим образом:

  loadRouter() {
    this.timing.start('Load Router');
    // 加载 router.js
    this.loadFile(this.resolveModule(path.join(this.options.baseDir, 'app/router')));
    this.timing.end('Load Router');
  },

Будет загружен только соответствующий проектapp/router.js, то есть маршрут должен иметь только один входной файл.Следующее Демо:

'use strict';

/**
 * @param {Egg.Application} app - egg application
 */
module.exports = app => {
  const { router, controller } = app;
  router.get('/', controller.home.index);
};

Приведенный выше код реализует маршрутизацию. Но мы просто добавили методы к соответствующим маршрутам, а как отслеживать изменения маршрута и вызывать разные методы? Это включает в себяkoaКак использовать промежуточное ПО, в дальнейшем мы разберем промежуточное ПО отдельно, аkoa-router

Суммировать

  1. Основные модули Egg включают Application(egg/lib/applicaton.js) -----> EggApplication(egg/lib/egg.js) -----> EggCore(egg-core/lib/egg.js) - - ---> KoaApplication(koa)
  2. eggjs загрузит файл конфигурации через loadConfig()
  loadConfig() {
    this.loadPlugin();
    super.loadConfig();
  }
  1. Ряд связанных конфигураций будет загружен с помощью метода load().
  load() {
    // app > plugin > core
    this.loadApplicationExtend();
    this.loadRequestExtend();
    this.loadResponseExtend();
    this.loadContextExtend();
    this.loadHelperExtend();
    // app > plugin
    this.loadCustomApp();
    // app > plugin
    this.loadService();
    // app > plugin > core
    this.loadMiddleware();
    // app
    this.loadController();
    // app
    this.loadRouter(); // Dependent on controllers
  }

строить планы

  1. Разберитесь с основным использованием Eggjs
  2. Использование промежуточного программного обеспечения
  3. Как использовать роутер
  4. Анализ яйцеклетки
  5. Анализ жизненного цикла яйца