Перейти к содержанию

Создание библиотек

📅 28.02.2022

На этой странице представлен концептуальный обзор того, как создавать и публиковать новые библиотеки для расширения функциональности Angular.

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

Начало работы

Используйте Angular CLI для создания нового скелета библиотеки в новом рабочем пространстве с помощью следующих команд.

1
2
3
ng new my-workspace --no-create-application
cd my-workspace
ng generate library my-lib

Название вашей библиотеки

Вы должны быть очень внимательны при выборе имени вашей библиотеки, если вы хотите опубликовать ее позже в публичном реестре пакетов, таком как npm. Смотрите Публикация вашей библиотеки.

Избегайте использования имен с префиксом ng-, например ng-library. Префикс ng- — это зарезервированное ключевое слово, используемое в фреймворке Angular и его библиотеках.

Префикс ngx- предпочтительнее в качестве соглашения, используемого для обозначения того, что библиотека подходит для использования с Angular.

Он также является отличным указанием для потребителей реестра для различения библиотек различных фреймворков JavaScript.

Команда ng generate создает папку projects/my-lib в вашем рабочем пространстве, которая содержит компонент и сервис внутри NgModule.

Подробнее о том, как структурируется библиотечный проект, читайте в разделе Файлы библиотечных проектов руководства Структура файлов проекта.

Используйте модель monorepo, чтобы использовать одно и то же рабочее пространство для нескольких проектов. См. раздел Настройка рабочего пространства для нескольких проектов.

Когда вы создаете новую библиотеку, файл конфигурации рабочего пространства, angular.json, обновляется проектом типа library.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
"projects": {
  …
  "my-lib": {
    "root": "projects/my-lib",
    "sourceRoot": "projects/my-lib/src",
    "projectType": "library",
    "prefix": "lib",
    "architect": {
      "build": {
        "builder": "@angular-devkit/build-angular:ng-packagr",
        …

Сборка, тестирование и линтинг проекта с помощью команд CLI:

1
2
3
ng build my-lib --configuration development
ng test my-lib
ng lint my-lib

Обратите внимание, что настроенный сборщик для проекта отличается от сборщика по умолчанию для проектов приложений. Этот конструктор, помимо прочего, гарантирует, что библиотека всегда собирается с помощью AOT-компилятора.

Чтобы сделать код библиотеки многократно используемым, необходимо определить для него публичный API. Этот "пользовательский уровень" определяет, что доступно потребителям вашей библиотеки.

Пользователь вашей библиотеки должен иметь доступ к публичной функциональности (такой как NgModules, провайдеры услуг и общие полезные функции) через единый путь импорта.

Публичный API для вашей библиотеки хранится в файле public-api.ts в папке вашей библиотеки. Все, что экспортируется из этого файла, становится общедоступным, когда ваша библиотека импортируется в приложение.

Используйте NgModule для демонстрации сервисов и компонентов.

Ваша библиотека должна поставлять документацию (обычно файл README) для установки и обслуживания.

Рефакторинг частей приложения в библиотеку

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

  • Такие объявления, как компоненты и пайпы, должны быть разработаны как без состояния, то есть они не зависят от внешних переменных и не изменяют их.

    Если вы полагаетесь на состояние, вам необходимо оценить каждый случай и решить, является ли это состоянием приложения или состоянием, которым будет управлять библиотека.

  • Любые наблюдаемые переменные, на которые компоненты подписываются внутри, должны быть очищены и утилизированы в течение жизненного цикла этих компонентов.

  • Компоненты должны раскрывать свое взаимодействие через входы для предоставления контекста и выходы для передачи событий другим компонентам.

  • Проверьте все внутренние зависимости.

    • Для пользовательских классов или интерфейсов, используемых в компонентах или сервисе, проверьте, не зависят ли они от дополнительных классов или интерфейсов, которые также должны быть перенесены.

    • Аналогично, если код вашей библиотеки зависит от сервиса, этот сервис необходимо перенести.

    • Если код вашей библиотеки или ее шаблоны зависят от других библиотек (таких как Angular Material, например), вы должны настроить вашу библиотеку с учетом этих зависимостей.

  • Подумайте о том, как вы предоставляете услуги клиентским приложениям.

    • Сервисы должны объявлять своих собственных провайдеров, а не объявлять провайдеров в NgModule или компоненте.

      Объявление провайдера делает этот сервис три-шатаемым.

      Такая практика позволяет компилятору не включать сервис в пакет, если он никогда не будет внедрен в приложение, импортирующее библиотеку.

      Подробнее об этом см. в Tree-shakable providers.

    • Если вы регистрируете глобальных провайдеров услуг или совместно используете провайдеров в нескольких NgModules, используйте шаблоны проектирования forRoot() и forChild(), предоставляемые RouterModule.

    • Если ваша библиотека предоставляет дополнительные услуги, которые могут использоваться не всеми клиентскими приложениями, поддерживайте надлежащее встряхивание дерева для этого случая, используя шаблон проектирования lightweight token

Интеграция с CLI с помощью схем генерации кода

Библиотека обычно включает повторно используемый код, определяющий компоненты, сервисы и другие артефакты Angular (пайпы, директивы), которые вы импортируете в проект. Библиотека упаковывается в пакет npm для публикации и совместного использования.

Этот пакет также может включать схемы, которые содержат инструкции по генерации или преобразованию кода непосредственно в вашем проекте, подобно тому, как CLI создает общий новый компонент с помощью ng generate component.

Схема, которая упакована с библиотекой, может, например, предоставить Angular CLI информацию, необходимую для создания компонента, который конфигурирует и использует определенную функцию или набор функций, определенных в этой библиотеке.

Примером может служить Angular Material's navigation schematic, который конфигурирует BreakpointObserver CDK и использует его с компонентами Material's MatSideNav и MatToolbar.

Создайте и включите следующие виды схем:

  • Включите схему установки, чтобы ng add мог добавить вашу библиотеку в проект.

  • Включите схему генерации в вашу библиотеку, чтобы ng generate мог поместить ваши определенные артефакты (компоненты, сервисы, тесты) в проект

  • Включите схему обновления, чтобы ng update мог обновлять зависимости вашей библиотеки и обеспечивать миграции для изменений в новых релизах.

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

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

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

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

В общем, чем сложнее настройка, тем полезнее схематический подход.

Для получения дополнительной информации смотрите Schematics Overview и Schematics for Libraries.

Публикация вашей библиотеки

Используйте Angular CLI и менеджер пакетов npm для создания и публикации вашей библиотеки в виде пакета npm.

Angular CLI использует инструмент под названием ng-packagr для создания пакетов из вашего скомпилированного кода, которые могут быть опубликованы в npm. Смотрите Создание библиотек с помощью Ivy для получения информации о форматах распространения, поддерживаемых ng-packagr, и руководства о том, как

выбрать правильный формат для вашей библиотеки.

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

1
2
3
ng build my-lib
cd dist/my-lib
npm publish

Управление активами в библиотеке

В вашу библиотеку Angular дистрибутив может включать дополнительные активы, такие как тематические файлы, Sass-миксины или документацию (например, журнал изменений). Для получения дополнительной информации копируйте активы в вашу библиотеку как часть сборки и встраивайте активы в стили компонентов.

При включении дополнительных активов, таких как миксины Sass или предварительно скомпилированные CSS. Вам нужно добавить их вручную в условный "exports" в package.json основной точки входа.

ng-packagr объединит написанные вручную "exports" с автоматически сгенерированными, позволяя авторам библиотек настраивать дополнительные подпути экспорта или пользовательские условия.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
"exports": {
".": {
    "sass": "./_index.scss",
},
"./theming": {
    "sass": "./_theming.scss"
},
"./prebuilt-themes/indigo-pink.css": {
    "style": "./prebuilt-themes/indigo-pink.css"
}
}

Вышеприведенное является выдержкой из @angular/material дистрибутива.

Одноранговые зависимости

Библиотеки Angular должны перечислять любые @angular/* зависимости, от которых зависит библиотека, как одноранговые зависимости. Это гарантирует, что когда модули запрашивают Angular, все они получают один и тот же модуль.

Если библиотека перечислит @angular/core в dependencies вместо peerDependencies, она может получить вместо него другой модуль Angular, что приведет к поломке вашего приложения.

Использование собственной библиотеки в приложениях

Вам не обязательно публиковать свою библиотеку в менеджере пакетов npm, чтобы использовать ее в том же рабочем пространстве, но вы должны сначала собрать ее.

Чтобы использовать собственную библиотеку в приложении:

  • Соберите библиотеку.

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

    1
    ng build my-lib
    
  • В своих приложениях импортируйте из библиотеки по имени:

    1
    import { myExport } from 'my-lib';
    

Сборка и восстановление вашей библиотеки

Шаг сборки важен, если вы не опубликовали свою библиотеку как пакет npm и затем не установили пакет обратно в свое приложение из npm. Например, если вы клонируете свой git-репозиторий и запустите npm install, ваш редактор покажет, что импорт my-lib отсутствует, если вы еще не собрали библиотеку.

Когда вы импортируете что-то из библиотеки в приложение Angular, Angular ищет соответствие между именем библиотеки и местом на диске. Когда вы устанавливаете пакет библиотеки, сопоставление находится в папке node_modules. Когда вы создаете свою собственную библиотеку, она должна найти отображение в ваших путях tsconfig.

Генерация библиотеки с помощью Angular CLI автоматически добавляет ее путь в файл tsconfig. Angular CLI использует пути tsconfig, чтобы указать системе сборки, где найти библиотеку.

Для получения дополнительной информации смотрите Path mapping overview.

Если вы обнаружили, что изменения в вашей библиотеке не отражаются в вашем приложении, вероятно, ваше приложение использует старую сборку библиотеки.

Вы можете пересобирать библиотеку каждый раз, когда вносите в нее изменения, но этот дополнительный шаг требует времени. Функциональность Incremental Builds улучшает опыт разработки библиотеки.

Каждый раз, когда файл изменяется, выполняется частичная сборка, в которую попадают измененные файлы.

Инкрементные сборки могут быть запущены как фоновый процесс в вашей среде разработки. Чтобы воспользоваться этой функцией, добавьте флаг --watch к команде сборки:

1
ng build my-lib --watch

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

  • Система сборки для приложений, @angular-devkit/build-angular, основана на webpack и включается во все новые проекты Angular CLI.
  • Система сборки для библиотек основана на ng-packagr. Она добавляется в ваши зависимости только тогда, когда вы добавляете библиотеку с помощью ng generate library my-lib.

Эти две системы сборки поддерживают разные вещи, и даже там, где они поддерживают одни и те же вещи, они делают это по-разному. Это означает, что источник TypeScript может привести к другому JavaScript-коду в собранной библиотеке, чем в собранном приложении.

По этой причине приложение, зависящее от библиотеки, должно использовать только сопоставления путей TypeScript, указывающие на собранную библиотеку. Сопоставления путей TypeScript не должны указывать на исходные файлы библиотеки .ts.

Публикация библиотек

Существует два формата распространения, которые можно использовать при публикации библиотеки:

Distribution formats Details
Partial-Ivy (recommended) Содержит переносимый код, который может быть использован приложениями Ivy, созданными с помощью любой версии Angular, начиная с v12.
Full-Ivy Содержит частные инструкции Angular Ivy, которые не гарантируют работу в разных версиях Angular. Этот формат требует, чтобы библиотека и приложение были собраны с точно одинаковой версией Angular. Этот формат полезен в средах, где весь код библиотеки и приложения собирается непосредственно из исходного кода.

Для публикации в npm используйте формат partial-Ivy, так как он стабилен между версиями Angular.

Избегайте компиляции библиотек с полным кодом Ivy при публикации в npm, поскольку генерируемые инструкции Ivy не являются частью публичного API Angular и могут меняться между версиями патчей.

Обеспечение совместимости версий библиотек

Версия Angular, используемая для создания приложения, всегда должна быть такой же или выше, чем версии Angular, используемые для создания любой из зависимых библиотек. Например, если у вас есть библиотека, использующая Angular версии 13, приложение, которое зависит от этой библиотеки, должно использовать Angular версии 13 или более поздней. Angular не поддерживает использование более ранней версии для приложения.

Если вы собираетесь опубликовать свою библиотеку на npm, скомпилируйте код с частичным плюсом, установив "compilationMode": "partial" в tsconfig.prod.json. Этот частичный формат стабилен между различными версиями Angular, поэтому его безопасно публиковать в npm. Код с таким форматом обрабатывается во время сборки приложения с помощью одной и той же версии компилятора Angular, что гарантирует, что приложение и все его библиотеки используют одну версию Angular.

Избегайте компиляции библиотек с полным кодом Ivy при публикации в npm, поскольку генерируемые инструкции Ivy не являются частью публичного API Angular и могут меняться между версиями патчей.

Если вы никогда раньше не публиковали пакет в npm, вам необходимо создать учетную запись пользователя. Подробнее читайте в Публикация пакетов npm.

Потребление частичного кода Ivy вне Angular CLI

Приложение устанавливает множество библиотек Angular из npm в каталог node_modules. Однако код из этих библиотек не может быть подключен непосредственно к собранному приложению, так как он не полностью скомпилирован. Чтобы завершить компиляцию, используйте компоновщик Angular.

Для приложений, не использующих Angular CLI, компоновщик доступен в виде плагина Babel. Плагин должен быть импортирован из @angular/compiler-cli/linker/babel.

Плагин Babel для компоновщика Angular поддерживает кэширование сборки, что означает, что библиотеки должны обрабатываться компоновщиком только один раз, независимо от других операций npm.

Пример интеграции плагина в пользовательскую сборку Webpack путем регистрации компоновщика как плагина Babel с помощью babel-loader.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import linkerPlugin from '@angular/compiler-cli/linker/babel';

export default {
    // ...
    module: {
        rules: [
            {
                test: /\.m?js$/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        plugins: [linkerPlugin],
                        compact: false,
                        cacheDirectory: true,
                    },
                },
            },
        ],
    },
    // ...
};

Angular CLI интегрирует плагин компоновщика автоматически, поэтому если потребители вашей библиотеки используют CLI, они могут установить библиотеки Ivy-native из npm без дополнительной настройки.

Комментарии