Как быстро приступить к работе с VUE3.0: войдите и изучите
. Nest предоставляет модульный механизм. Внедрение зависимостей завершается определением поставщиков, импорта, экспорта и конструкторов поставщиков в декораторе модуля, а разработка всего приложения организуется с помощью. дерево модулей. Нет абсолютно никаких проблем с прямым запуском приложения в соответствии с соглашениями самого фреймворка. Однако я чувствую, что мне не хватает более четкого и систематического понимания внедрения зависимостей, инверсии управления, модулей, провайдеров, метаданных, связанных декораторов и т. д., объявленных фреймворком.
- Зачем нужна инверсия управления?
- Что такое внедрение зависимостей?
- Что делает декоратор?
- Каковы принципы реализации провайдеров, импорта и экспорта в модулях (@Module)?
Я вроде бы могу это понять и оценить, но позвольте мне объяснить это ясно с самого начала, я не могу объяснить это ясно. Поэтому я провел небольшое исследование и написал эту статью. С этого момента мы начинаем с нуля и вводим основной текст.
1.1 Экспресс, Коа
Процесс развития языка и его технического сообщества должен постепенно обогащаться и развиваться снизу вверх, подобно тому, как корни деревьев медленно разрастаются в ветви, а затем наполняются листьями. Ранее в Nodejs появились базовые фреймворки веб-сервисов, такие как Express и Koa. Способен предоставить самые базовые сервисные возможности. На основе такой структуры в сообществе стало рождаться большое количество промежуточного программного обеспечения и плагинов, предоставляющих более богатые услуги для платформы. Нам необходимо самим организовывать зависимости приложений и строить каркасы приложений, что является гибким и громоздким, а также требует определенной рабочей нагрузки.
Позже в процессе разработки появились некоторые структуры с более эффективным производством и более унифицированными правилами, что положило начало новому этапу.
1.2 EggJs и Nestjs
Чтобы лучше адаптироваться к приложениям быстрого производства, унифицировать стандарты и сделать их доступными «из коробки», были разработаны такие платформы, как EggJs, NestJs и Midway. Платформа этого типа абстрагирует реализацию приложения в универсальный и расширяемый процесс, реализуя базовый жизненный цикл. Нам нужно только следовать методу настройки, предусмотренному платформой, чтобы упростить реализацию приложения. Фреймворк реализует управление процессом программы, и нам нужно только собрать наши части в соответствующем месте. Это больше похоже на работу на конвейере. Каждый процесс четко разделен, что позволяет сэкономить много затрат на внедрение.
1.3.
Вышеупомянутые два этапа являются лишь предзнаменованием. Мы можем примерно понять, что обновление платформы повышает эффективность производства. Для достижения обновления платформы в Nest будут представлены некоторые дизайнерские идеи и шаблоны. внедрение зависимостей и концепции метапрограммирования, поговорим об этом ниже.
2.1 Внедрение зависимостей
Приложение на самом деле представляет собой множество абстрактных классов, которые реализуют все функции приложения путем вызова друг друга. По мере увеличения сложности кода и функций приложения проект определенно будет становиться все сложнее поддерживать, поскольку классов становится все больше, а отношения между ними становятся все более сложными.
Например, если мы используем Koa для разработки нашего приложения, сам Koa в основном реализует набор основных возможностей веб-сервиса. В процессе реализации приложения мы определим множество классов, методы создания экземпляров и взаимозависимости этих классов. свободно организовываться и контролироваться нами в логике кода. Создание экземпляра каждого класса создается нами вручную, и мы можем контролировать, создается ли экземпляр класса только один раз, а затем используется совместно, или он создается каждый раз. Следующий класс B зависит от A. Каждый раз, когда создается экземпляр B, экземпляр A будет создаваться один раз, поэтому для каждого экземпляра B A является экземпляром, который не является общим.
класс А{} // Б класс Б{ конструктор(){ this.a = новый A(); } }
C ниже — это полученный внешний экземпляр, поэтому несколько экземпляров C совместно используют экземпляр app.a.
класс А{} // С константное приложение = {}; app.a = новый A(); класс С{ конструктор(){ this.a = app.a; } }
Следующий D передается через параметр конструктора. Вы можете каждый раз передавать необщий экземпляр или общий экземпляр app.a (D и F совместно используют app.a), в зависимости от способа. теперь это параметр. Я также могу передать экземпляр класса X.
класс А{} класс X{} //Д константное приложение = {}; app.a = новый A(); класс D{ конструктор(а){ это.а = а; } } класс F{ конструктор(а){ это.а = а; } } новый D (прил.а) новый F(прил.а)
новый
D(новый
Внедрение через конструктор (передача по значению) — это только один метод реализации. Его также можно передать, реализовав вызов метода set или любого другого метода, если внешняя зависимость может быть передана во внутреннюю. Это действительно так просто.
класс А{} //Д класс D{ setDep(а){ это.а = а; } } константа d = новый D() d.setDep(new A())
2.2 Все во внедрении зависимостей?
По ходу итерации оказывается, что зависимости B будут меняться в соответствии с различными предварительными условиями. Например, первое предварительное условие this.a
должно быть передано в экземпляре A, а второе предварительное условие this.a
должно быть передано в экземпляре X. В это время мы начнем заниматься настоящей абстракцией. Мы преобразуем его в метод внедрения зависимостей, как описано выше.
В первые дни, когда мы реализовали приложение, мы реализовали метод записи классов B и C, если он отвечал потребностям того времени. Это само по себе не было проблемой. После того, как проект повторялся в течение нескольких лет, эта часть была. кода не обязательно будет затронут. Если мы рассмотрим дальнейшее расширение, это повлияет на эффективность разработки и может оказаться бесполезным. Поэтому большую часть времени мы сталкиваемся со сценариями, требующими абстракции, а затем абстрактного преобразования части кода.
// класс B{ перед преобразованием конструктор(){ this.a = новый A(); } } новый Б() //Класс D{ после преобразования конструктор(а){ это.а = а; } } новый D (новый A())новый D
(
новыезатраты на внедрение.
Этот пример приведен здесь, чтобы проиллюстрировать это в модели развития без каких-либо ограничений или правил. Мы можем свободно писать код для управления зависимостями между различными классами. В полностью открытой среде это очень свободно. Это примитивная эпоха подсечного земледелия. Поскольку не существует фиксированной модели разработки кода и плана действий высшего уровня, поскольку разные разработчики вмешиваются или один и тот же разработчик пишет код в разное время, отношения зависимости будут сильно различаться по мере роста кода. Очевидно, что общий экземпляр может создаваться несколько раз. , трата памяти. Из кода трудно увидеть полную структуру зависимостей, и код может стать очень сложным в обслуживании.
Затем каждый раз, когда мы определяем класс, мы пишем его в соответствии с методом внедрения зависимостей и пишем его как D. Затем процесс абстракции C и B углубляется, что делает последующее расширение более удобным и снижает стоимость преобразования. Итак, это называется All in 依赖注入
, то есть все наши зависимости реализуются посредством внедрения зависимостей.
Однако затраты на раннюю реализацию снова становятся высокими, и трудно достичь единства и постоянства в командном сотрудничестве. В конечном итоге реализация может оказаться неудачной. обязательно будет достигнута.
2.3 Инверсия управления
Теперь, когда мы договорились об унифицированном использовании внедрения зависимостей, можем ли мы реализовать контроллер нижнего уровня посредством базовой инкапсуляции структуры и договориться о правиле конфигурации зависимостей. Контроллер будет управлять процессом создания экземпляров на основе мы определили конфигурацию зависимостей и совместное использование зависимостей, чтобы помочь нам добиться управления классами. Этот шаблон проектирования называется инверсией управления .
Инверсия контроля может быть трудной для понимания, когда вы слышите это впервые. Что означает контроль? Что было перевернуто?
Предполагается, что это связано с тем, что разработчики использовали такие структуры с самого начала, не пережили последнюю «эпоху Express и Koa» и им не хватает ударов старого общества. В сочетании с перевернутой формулировкой программа выглядит очень абстрактной и трудной для понимания.
Как мы упоминали ранее, при реализации приложений Koa все классы полностью контролируются нами, поэтому его можно рассматривать как обычный метод программного управления, поэтому мы называем его: управление вращением вперед. Мы используем Nest, который реализует набор контроллеров внизу. Нам нужно только написать код конфигурации согласно соглашению во время фактического процесса разработки, а программа-фреймворк поможет нам управлять внедрением зависимостей классов, поэтому мы называем это: инверсия управления.
Суть заключается в том, чтобы передать процесс реализации программы фреймворку для единого управления, а полномочия по контролю от разработчика передать фреймворку.
Управление вращением вперед: программа чисто ручного управления от разработчика
Инверсия управления: рамочное программное управление
Если привести реальный пример, человек едет на работу сам, и его цель — добраться до компании. Он ездит сам и контролирует свой маршрут. А если он передаст управление автомобилем и успеет на автобус, ему останется только выбрать соответствующий маршрутный автобус, чтобы доехать до предприятия. Только в плане контроля люди раскрепощены. Им нужно только помнить, на каком автобусе сесть. Шанс совершить ошибку также снижается, и люди становятся гораздо более расслабленными. Система шин — это контроллер, а линии шины — это согласованные конфигурации.
Я думаю, что благодаря приведенному выше фактическому сравнению я смогу понять инверсию контроля.
2.4. Резюме:
от Koa до Nest, от клиентского JQuery до Vue React. Фактически, все они реализуются шаг за шагом посредством инкапсуляции структуры, чтобы решить проблемы неэффективности предыдущей эпохи.
Вышеупомянутая разработка приложения Koa использует очень примитивный способ управления зависимостями и созданием экземпляров, который похож на операционную систему JQuery во внешнем интерфейсе. Этот очень примитивный способ называется пересылкой управления, а Vue React похож на то, что предоставляет Nest. контроллер, все их можно назвать инверсией управления. Это также мое личное понимание. Если есть какие-то проблемы, я надеюсь, что Бог укажет на них.
Давайте поговорим о модуле @Module в Nest. Для внедрения зависимостей и инверсии управления он необходим в качестве носителя.
Nestjs реализует инверсию управления и соглашается настраивать импорт, экспорт и поставщиков модуля (@module) для управления поставщиком, который представляет собой внедрение зависимостей класса.
Провайдеры можно понимать как регистрацию и создание экземпляров классов в текущем модуле. Следующие экземпляры A и B создаются в текущем модуле. Если B ссылается на A в конструкторе, это относится к экземпляру A текущего ModuleD.
импортировать {Модуль} из '@nestjs/common'; импортировать { ModuleX } из './moduleX'; импортировать {A} из './A'; импортировать { B } из './B'; @Модуль({ импорт: [ModuleX], провайдеры: [A,B], экспорт: [А] }) класс экспорта ModuleD {} // Б класс Б{ конструктор(а:А){ это.а = а; } }
exports
относится к классам, экземпляры которых созданы в providers
текущего модуля, как к классам, которые могут совместно использоваться внешними модулями. Например, когда создается экземпляр класса C ModuleF, я хочу напрямую внедрить экземпляр класса A ModuleD. Просто установите экспорт A в ModuleD и импортируйте ModuleD через imports
в ModuleF.
Согласно следующему методу записи, программа управления инверсией автоматически просканирует зависимости. Сначала проверьте, есть ли провайдер A в провайдерах вашего собственного модуля. Если нет, найдите экземпляр A в импортированном ModuleD. найден, получите A экземпляра ModuleD, который будет добавлен в экземпляр C.
импортировать {Модуль} из '@nestjs/common'; импортировать {ModuleD} из './moduleD'; импортировать { C } из './C'; @Модуль({ импорт: [ModuleD], провайдеры: [С], }) класс экспорта ModuleF {} // С класс С { конструктор(а:А){ это.а = а; } }
Поэтому, если вы хотите, чтобы внешний модуль использовал экземпляр класса текущего модуля, вы должны сначала определить класс создания экземпляра в providers
текущего модуля, а затем определить и экспортировать этот класс, в противном случае будет сообщено об ошибке.
//Исправим @Module({ провайдеры: [А], экспорт: [А] }) //Ошибка @Модуль({ провайдеры: [], экспорт: [А] })
Оглядываясь назад на процесс поиска экземпляровпоследующего дополнительного
модуля, можно увидеть, что он действительно немного неясен. Суть в том, что экземпляры классов в поставщиках будут созданы, и после создания экземпляров они станут поставщиками. Будут созданы экземпляры только классов в поставщиках в модуле, а экспорт и импорт — это всего лишь конфигурации организационных отношений. Модуль отдаст приоритет использованию собственного поставщика. Если нет, проверьте, есть ли у импортированного модуля соответствующий поставщик.
Позвольте мне упомянуть некоторые моменты
экспорта ts {.
конструктор (частное а: А) { } }
Поскольку TypeScript поддерживает параметры конструктора (частные, защищенные, общедоступные, только для чтения), которые неявно и автоматически определяются как атрибуты класса (свойство параметра), нет необходимости использовать this.a = a
. Так написано в Nest.
Концепция метапрограммирования отражена в среде Nest. Инверсия управления и декораторы являются реализацией метапрограммирования. Грубо говоря, суть метапрограммирования по-прежнему заключается в программировании, но в середине есть несколько абстрактных программ. Эта абстрактная программа может идентифицировать метаданные (например, данные объекта в @Module), что на самом деле является возможностью расширения, которая может использовать другие. программы как данные для обработки. Когда мы пишем такие абстрактные программы, мы занимаемся метапрограммированием.
4.1 Метаданные
Метаданные часто упоминаются в документах Nest. Концепция метаданных может сбить с толку, когда вы видите ее впервые. К ней нужно привыкнуть и понять ее со временем, поэтому вам не нужно слишком углубляться. запутался.
Определение метаданных следующее: данные, описывающие данные, в основном информация, описывающая атрибуты данных, а также данные, описывающие программы.
exports、providers、imports、controllers
настроенные @Module в Nest, являются метаданными , поскольку это данные, используемые для описания взаимосвязей программы. Эта информация о данных не является фактическими данными, отображаемыми конечному пользователю, но считывается и распознается пользователем. рамочная программа.
4.2 Декоратор Nest
Если вы посмотрите исходный код декоратора в Nest, вы обнаружите, что почти каждый декоратор сам определяет метаданные только посредством отражения метаданных.
Функция экспортадекоратора @Injectable
Injectable(options?: InjectableOptions): ClassDecorator { return (цель: объект) => { Reflect.defineMetadata(INJECTABLE_WATERMARK, true, target); Reflect.defineMetadata(SCOPE_OPTIONS_METADATA, параметры, цель); }; }
Здесь используется концепция отражения, и ее относительно легко понять. Возьмем в качестве примера декоратор providers
для определения providers
метаданных. Он просто передает классы в массив providers
. автоматически использоваться программой платформы. Создание экземпляра становится поставщиком, и разработчикам не нужно явно выполнять создание экземпляров и внедрение зависимостей. Класс становится поставщиком только после того, как его экземпляр будет создан в модуле. Классы в providers
отражаются и становятся поставщиками. Инверсия управления — это используемая технология отражения.
Другим примером является ORM (реляционное сопоставление объектов) в базе данных. Чтобы использовать ORM, вам нужно только определить поля таблицы, и библиотека ORM автоматически преобразует данные объекта в операторы SQL.
константные данные = TableModel.build(); данные.время = 1; data.browser = 'хром'; данные.сохранить(); // SQL: INSERT INTO tableName (time,browser) [{"time":1,"browser":"chrome"}]
Библиотека ORM использует технологию отражения, поэтому пользователям нужно обращать внимание только на сами данные поля, и объект, отражение библиотеки ORM, становится оператором выполнения SQL. Разработчикам нужно сосредоточиться только на полях данных, и им не нужно писать SQL.
4.3 Reflect-Metadata
Reflect-Metadata — это библиотека отражений, которую Nest использует для управления метаданными. Reflection-metadata использует WeakMap для создания единого глобального экземпляра, а также устанавливает и получает метаданные декорированного объекта (класса, метода и т. д.) с помощью методов set и get.
// Просто взгляните var _WeakMap = !usePolyfill && typeof WeakMap === "function" : CreateWeakMapPolyfill(); вар Метаданные = новый _WeakMap(); функция defineMetadata(){ OrdinaryDefineOwnMetadata(){ GetOrCreateMetadataMap(){ вар targetMetadata = Metadata.get(O); если (IsUndefine(targetMetadata)) { если (!Создать) вернуть неопределенное значение; targetMetadata = новый _Map(); Metadata.set(O, targetMetadata); } вар метаданныхMap = targetMetadata.get(P); если (IsUndefine(metadataMap)) { если (!Создать) вернуть неопределенное значение; метаданныеMap = новая _Map(); targetMetadata.set(P, MetadataMap); } вернуть карту метаданных; } } }
Reflection-metadata сохраняет метаданные украшенного человека в глобальном одноэлементном объекте для унифицированного управления. reflect-metadata не реализует конкретное отражение, но предоставляет библиотеку инструментов, помогающую реализовать отражение.
, давайте посмотрим на предыдущие вопросы.
Зачем нужна инверсия управления?
Что такое внедрение зависимостей?
Что делает декоратор?
Каковы принципы реализации провайдеров, импорта и экспорта в модулях (@Module)?
1 и 2. Я думаю, что уже дал это понять. Если это все еще немного расплывчато, я предлагаю вам вернуться, прочитать его еще раз и обратиться к другим статьям, которые помогут вам понять знания через размышления разных авторов.
5.1 Проблема [3 4] Обзор:
Nest использует технологию отражения для реализации инверсии управления и предоставляет возможности метапрограммирования. Разработчики используют декоратор @Module для украшения классов и определения метаданных (поставщикиимпортэкспорт), при этом метаданные хранятся в глобальном пространстве. объект (с использованием библиотеки отражающих метаданных). После запуска программы управляющая программа внутри платформы Nest считывает и регистрирует дерево модулей, сканирует метаданные и создает экземпляр класса, который становится поставщиком, и предоставляет его во всех модулях в соответствии с определением поставщиковимпортовэкспортов в метаданных модуля. Найдите экземпляры (провайдеры) других зависимых классов текущего класса и внедрите их через конструктор после их обнаружения.
В этой статье много концепций, и она не содержит слишком подробного анализа. Для понимания концепций требуется время. Если вы в данный момент не понимаете их полностью, не беспокойтесь. Хорошо, вот и все. Эта статья все же потребовала много усилий. Друзья, которым она понравилась, надеются, что вы сможете подключиться три раза одним щелчком мыши~