Эта страница даёт концептуальный обзор того, как создавать и публиковать новые библиотеки для расширения функциональности Angular.
Если вы обнаруживаете, что нужно решать одну и ту же задачу в более чем одном приложении (или хотите поделиться решением с другими разработчиками), у вас есть кандидат на библиотеку. Простой пример — кнопка, отправляющая пользователей на сайт компании, которая включалась бы во все приложения, которые строит ваша компания.
Начало работы
Используйте Angular CLI для генерации скелета новой библиотеки в новом workspace следующими командами.
ng new my-workspace --no-create-application
cd my-workspace
ng generate library my-lib
Naming your library
Будьте очень осторожны при выборе имени библиотеки, если планируете позже опубликовать её в публичном реестре пакетов, таком как npm. См. Публикация библиотеки.
Избегайте использования имени с префиксом ng-, например ng-library.
Префикс ng- — зарезервированное ключевое слово, используемое фреймворком Angular и его библиотеками.
Префикс ngx- предпочтителен как соглашение, обозначающее, что библиотека подходит для использования с Angular.
Это также отличный сигнал для потребителей реестра, чтобы отличать библиотеки разных JavaScript-фреймворков.
Команда ng generate создаёт папку projects/my-lib в workspace, которая содержит компонент.
ПОЛЕЗНО: Подробнее о структуре проекта библиотеки см. в разделе Файлы проекта библиотеки руководства Структура файлов проекта.
Используйте модель monorepo, чтобы использовать один workspace для нескольких проектов. См. Настройка multi-project workspace.
При генерации новой библиотеки файл конфигурации workspace angular.json обновляется проектом типа library.
"projects": {
…
"my-lib": {
"root": "projects/my-lib",
"sourceRoot": "projects/my-lib/src",
"projectType": "library",
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular/build:ng-packagr",
…
Соберите, протестируйте и проверьте lint проекта командами CLI:
ng build my-lib --configuration development
ng test my-lib
ng lint my-lib
Обратите внимание, что настроенный builder для проекта отличается от builder по умолчанию для проектов приложений. Этот builder, среди прочего, гарантирует, что библиотека всегда собирается с AOT-компилятором.
Чтобы сделать код библиотеки переиспользуемым, необходимо определить для неё публичный API. Этот «пользовательский слой» определяет, что доступно потребителям вашей библиотеки. Пользователь библиотеки должен иметь возможность получать доступ к публичной функциональности (например, service providers и общие утилитарные функции) через единый путь импорта.
Публичный API библиотеки поддерживается в файле public-api.ts в папке библиотеки.
Всё, экспортированное из этого файла, становится публичным при импорте библиотеки в приложение.
Библиотека должна предоставлять документацию (обычно файл README) по установке и поддержке.
Рефакторинг частей приложения в библиотеку
Чтобы сделать решение переиспользуемым, нужно скорректировать его так, чтобы оно не зависело от кода, специфичного для приложения. Вот на что стоит обратить внимание при переносе функциональности приложения в библиотеку.
Объявления вроде компонентов и pipes следует проектировать как stateless, то есть они не опираются на внешние переменные и не изменяют их. Если вы опираетесь на состояние, нужно оценить каждый случай и решить, это состояние приложения или состояние, которым управляла бы библиотека.
Любые observables, на которые компоненты подписываются внутри, должны очищаться и освобождаться в течение жизненного цикла этих компонентов
Компоненты должны экспонировать взаимодействия через inputs для предоставления контекста и outputs для сообщения о событиях другим компонентам
Проверьте все внутренние зависимости.
- Для пользовательских классов или интерфейсов, используемых в компонентах или сервисе, проверьте, зависят ли они от дополнительных классов или интерфейсов, которые также нужно мигрировать
- Аналогично, если код библиотеки зависит от сервиса, этот сервис нужно мигрировать
- Если код библиотеки или её шаблоны зависят от других библиотек (например, Angular Material), необходимо настроить библиотеку с этими зависимостями
Подумайте, как вы предоставляете сервисы клиентским приложениям.
Сервисы должны объявлять собственные providers, а не объявлять providers в NgModule или компоненте. Объявление provider делает сервис tree-shakable. Эта практика позволяет компилятору исключить сервис из бандла, если он никогда не внедряется в приложение, импортирующее библиотеку. Подробнее об этом см. в Tree-shakable providers.
Если вы регистрируете глобальные service providers, экспонируйте provider-функцию
provideXYZ().Если библиотека предоставляет необязательные сервисы, которые могут не использоваться всеми клиентскими приложениями, поддержите корректный tree-shaking для этого случая с помощью паттерна lightweight token
Интеграция с CLI с помощью schematics генерации кода
Библиотека обычно включает переиспользуемый код, определяющий компоненты, сервисы и другие артефакты Angular (pipes, директивы), которые вы импортируете в проект.
Библиотека упаковывается в npm-пакет для публикации и обмена.
Этот пакет также может включать schematics, предоставляющие инструкции для генерации или трансформации кода напрямую в проекте, так же как CLI создаёт общий новый компонент с ng generate component.
Schematic, упакованный с библиотекой, может, например, предоставить Angular CLI информацию, необходимую для генерации компонента, который настраивает и использует конкретную возможность или набор возможностей, определённых в этой библиотеке.
Один пример этого — navigation schematic Angular Material, который настраивает BreakpointObserver CDK и использует его с компонентами Material MatSideNav и MatToolbar.
Создавайте и включайте следующие виды schematics:
- Включите installation schematic, чтобы
ng addмог добавить библиотеку в проект - Включите generation schematics в библиотеку, чтобы
ng generateмог создавать определённые вами артефакты (компоненты, сервисы, тесты) в проекте - Включите update schematic, чтобы
ng updateмог обновлять зависимости библиотеки и предоставлять миграции для breaking changes в новых релизах
Что включать в библиотеку, зависит от задачи.
Например, можно определить schematic для создания dropdown, предварительно заполненного готовыми данными, чтобы показать, как добавить его в приложение.
Если нужен dropdown, который каждый раз содержал бы разные переданные значения, библиотека могла бы определить schematic для его создания с данной конфигурацией.
Разработчики затем могли бы использовать ng generate для настройки экземпляра для собственного приложения.
Предположим, вы хотите прочитать файл конфигурации, а затем сгенерировать форму на основе этой конфигурации. Если этой форме нужна дополнительная кастомизация разработчиком, использующим библиотеку, лучше всего подойдёт schematic. Однако если форма всегда будет одинаковой и не потребует большой кастомизации разработчиками, можно создать динамический компонент, который принимает конфигурацию и генерирует форму. В общем, чем сложнее кастомизация, тем полезнее подход со schematic.
Дополнительную информацию см. в Обзор Schematics и Schematics для библиотек.
Публикация библиотеки
Используйте Angular CLI и менеджер пакетов npm для сборки и публикации библиотеки как npm-пакета.
Angular CLI использует инструмент ng-packagr для создания пакетов из скомпилированного кода, которые можно публиковать в npm.
См. Сборка библиотек с Ivy для информации о форматах дистрибуции, поддерживаемых ng-packagr, и рекомендаций по выбору
правильного формата для библиотеки.
Библиотеки для дистрибуции всегда следует собирать с конфигурацией production.
Это гарантирует, что сгенерированный вывод использует подходящие оптимизации и корректный формат пакета для npm.
ng build my-lib
cd dist/my-lib
npm publish
Управление ресурсами в библиотеке
В Angular-библиотеке дистрибутив может включать дополнительные ресурсы вроде файлов темизации, Sass mixins или документации (например, changelog). Дополнительную информацию см. в копирование ресурсов в библиотеку как часть сборки и встраивание ресурсов в стили компонентов.
ВАЖНО: При включении дополнительных ресурсов вроде Sass mixins или предварительно скомпилированного CSS
их нужно вручную добавить в условные "exports" в package.json первичной точки входа.
ng-packagr объединит рукописные "exports" с автогенерированными, позволяя авторам библиотек настраивать дополнительные export subpaths или пользовательские условия.
"exports": {
".": {
"sass": "./_index.scss",
},
"./theming": {
"sass": "./_theming.scss"
},
"./prebuilt-themes/indigo-pink.css": {
"style": "./prebuilt-themes/indigo-pink.css"
}
}
Выше — выдержка из дистрибутива @angular/material.
Peer dependencies
Angular-библиотеки должны перечислять любые зависимости @angular/*, от которых зависит библиотека, как peer dependencies.
Это гарантирует, что когда модули запрашивают Angular, все получают один и тот же модуль.
Если библиотека перечисляет @angular/core в dependencies вместо peerDependencies, она может получить другой модуль Angular, что сломает приложение.
Использование собственной библиотеки в приложениях
Не обязательно публиковать библиотеку в менеджере пакетов npm, чтобы использовать её в том же workspace, но сначала её нужно собрать.
Чтобы использовать собственную библиотеку в приложении:
- Соберите библиотеку. Нельзя использовать библиотеку до её сборки.
ng build my-lib
- В приложениях импортируйте из библиотеки по имени:
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.
Библиотеку можно пересобирать при каждом изменении, но этот дополнительный шаг занимает время. Функциональность инкрементальных сборок улучшает опыт разработки библиотек. При каждом изменении файла выполняется частичная сборка, выдающая изменённые файлы.
Инкрементальные сборки можно запускать как фоновый процесс в среде разработки.
Чтобы воспользоваться этой возможностью, добавьте флаг --watch к команде сборки:
ng build my-lib --watch
ВАЖНО: Команда CLI build использует другой builder и вызывает другой инструмент сборки для библиотек, чем для приложений.
- Система сборки для приложений,
@angular/build, основана наesbuildи включена во все новые проекты Angular CLI - Система сборки для библиотек основана на
ng-packagr. Она добавляется в зависимости только при добавлении библиотеки с помощьюng generate library my-lib.
Две системы сборки поддерживают разное, и даже там, где они поддерживают одно и то же, делают это по-разному. Это означает, что исходный код TypeScript может привести к разному JavaScript-коду в собранной библиотеке, чем в собранном приложении.
По этой причине приложение, зависящее от библиотеки, должно использовать только TypeScript path mappings, указывающие на собранную библиотеку.
TypeScript path mappings не должны указывать на исходные файлы .ts библиотеки.
Связывание библиотек для локальной разработки
В этом разделе объясняется, как использовать возможность локального связывания менеджера пакетов
(например npm link или pnpm link) для тестирования автономной Angular-библиотеки с внешним приложением во время
локальной разработки без опоры на структуру monorepo workspace или публикации в реестр npm.
ПРИМЕЧАНИЕ: Если библиотека и приложение находятся в одном Angular workspace (настройка monorepo), стандартный workflow monorepo автоматически обрабатывает связывание и обычно эффективнее. Этот подход локального связывания лучше всего подходит, когда:
- Вы разрабатываете автономную библиотеку и нужно тестировать изменения с внешним потребляющим приложением.
- Вы тестируете изменения библиотеки в потребляющем приложении вне monorepo workspace.
Настройка потребляющего приложения
Чтобы использовать связанные библиотеки, нужно настроить файл angular.json приложения со следующими настройками:
{
"projects": {
"your-app": {
"architect": {
"build": {
"builder": "@angular/build:application",
"options": {
"preserveSymlinks": true
},
"configurations": {
"development": {
"sourceMap": {
"scripts": true,
"styles": true,
"vendor": true
}
}
}
},
"serve": {
"builder": "@angular/build:dev-server",
"options": {
"prebundle": {
"exclude": ["my-lib"]
}
}
}
}
}
}
}
Объяснение опций конфигурации:
preserveSymlinks: true: указывает системе сборки следовать symlink'ам, созданным командой связывания менеджера пакетов, вместо разрешения в исходное расположение symlink. Это необходимо, чтобы избежать нескольких копий зависимых node-пакетов.sourceMap.vendor: включение vendor source maps (особенноvendor: true) для более удобной отладки связанного кода библиотеки.prebundle.exclude: по умолчанию Angular CLI может предварительно бандлить все node-зависимости. Исключение библиотеки гарантирует, что связанный исходный код правильно отслеживается и пересобирается при изменениях.
Публикация библиотек
При публикации библиотеки есть два формата дистрибуции:
| Форматы дистрибуции | Подробности |
|---|---|
| Partial-Ivy (рекомендуется) | Содержит переносимый код, который могут потреблять Ivy-приложения, собранные с любой версией Angular начиная с v12. |
| Full-Ivy | Содержит частные инструкции Angular Ivy, которые не гарантированно работают между разными версиями Angular. Этот формат требует, чтобы библиотека и приложение были собраны с точно той же версией Angular. Этот формат полезен для окружений, где весь код библиотеки и приложения собирается напрямую из исходников. |
Для публикации в npm используйте формат partial-Ivy, так как он стабилен между patch-версиями Angular.
Избегайте компиляции библиотек с full-Ivy кодом при публикации в npm, потому что сгенерированные Ivy-инструкции не являются частью публичного API Angular и поэтому могут меняться между patch-версиями.
Обеспечение совместимости версий библиотеки
Версия Angular, используемая для сборки приложения, всегда должна быть той же или выше, чем версии Angular, использованные для сборки любых зависимых библиотек. Например, если у вас была библиотека на Angular версии 13, приложение, зависящее от этой библиотеки, должно использовать Angular версии 13 или новее. Angular не поддерживает использование более ранней версии для приложения.
Если вы намерены опубликовать библиотеку в npm, компилируйте с partial-Ivy кодом, установив "compilationMode": "partial" в tsconfig.prod.json.
Этот partial-формат стабилен между разными версиями Angular, поэтому его безопасно публиковать в npm.
Код в этом формате обрабатывается во время сборки приложения с той же версией компилятора Angular, гарантируя, что приложение и все его библиотеки используют одну версию Angular.
Избегайте компиляции библиотек с full-Ivy кодом при публикации в npm, потому что сгенерированные Ivy-инструкции не являются частью публичного API Angular и поэтому могут меняться между patch-версиями.
Если вы никогда раньше не публиковали пакет в npm, необходимо создать учётную запись пользователя. Подробнее в Publishing npm Packages.
Потребление partial-Ivy кода вне Angular CLI
Приложение устанавливает многие Angular-библиотеки из npm в каталог node_modules.
Однако код в этих библиотеках нельзя напрямую объединить в бандл вместе с собранным приложением, так как он не полностью скомпилирован.
Чтобы завершить компиляцию, используйте Angular linker.
Для приложений, не использующих Angular CLI, linker доступен как плагин Babel.
Плагин импортируется из @angular/compiler-cli/linker/babel.
Babel-плагин Angular linker поддерживает кэширование сборки, то есть библиотеки нужно обработать linker'ом только один раз, независимо от других операций npm.
Пример интеграции плагина в пользовательскую сборку webpack путём регистрации linker как плагина Babel с помощью babel-loader.
webpack.config.mjs
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 интегрирует плагин linker автоматически, поэтому если потребители библиотеки используют CLI, они могут устанавливать Ivy-native библиотеки из npm без дополнительной конфигурации.