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

Введение в сервисы и инъекцию зависимостей

📅 28.02.2022

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

Он должен делать что-то конкретное и делать это хорошо.

Angular отличает компоненты от сервисов для повышения модульности и возможности повторного использования.

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

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

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

Примеры сервисов

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
export class Logger {
    log(msg: any) {
        console.log(msg);
    }
    error(msg: any) {
        console.error(msg);
    }
    warn(msg: any) {
        console.warn(msg);
    }
}

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

Этот сервис, в свою очередь, может зависеть от сервиса HttpClient для асинхронного получения героев с сервера.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
export class HeroService {
    private heroes: Hero[] = [];

    constructor(
        private backend: BackendService,
        private logger: Logger
    ) {}

    getHeroes() {
        this.backend.getAll(Hero).then((heroes: Hero[]) => {
            this.logger.log(
                `Fetched ${heroes.length} heroes.`
            );
            this.heroes.push(...heroes); // fill cache
        });
        return this.heroes;
    }
}

Инъекция зависимостей (DI)

Service

Инъекция зависимостей (DI) — это часть фреймворка Angular, которая предоставляет компонентам доступ к сервисам и другим ресурсам. Angular предоставляет возможность инжектировать сервис в компонент, чтобы предоставить этому компоненту доступ к сервису.

Декоратор @Injectable() определяет класс как сервис в Angular и позволяет Angular внедрять его в компонент как зависимость. Аналогично, декоратор @Injectable() указывает, что компонент, класс, пайп или NgModule имеет зависимость от сервиса.

  • Инжектор является основным механизмом.

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

    Вам не нужно создавать инжекторы.

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

  • Провайдер — это объект, который указывает инжектору, как получить или создать зависимость.

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

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

Когда Angular создает новый экземпляр класса компонента, он определяет, какие сервисы или другие зависимости нужны этому компоненту, глядя на типы параметров конструктора. Например, конструктор HeroListComponent нуждается в HeroService.

1
constructor(private service: HeroService) { }

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

Когда все запрошенные сервисы будут разрешены и возвращены, Angular может вызвать конструктор компонента с этими сервисами в качестве аргументов.

Процесс инжекции HeroService выглядит примерно так.

Service

Предоставление услуг

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

Вы регистрируете провайдеров в метаданных сервиса (в декораторе @Injectable()), или в метаданных @NgModule()или@Component().

  • По умолчанию команда Angular CLI ng generate service регистрирует провайдер с корневым инжектором для вашего сервиса, включая метаданные провайдера в декоратор @Injectable().

    В учебнике этот метод используется для регистрации провайдера определения класса HeroService.

    1
    2
    3
    @Injectable({
        providedIn: 'root',
    })
    

    Когда вы предоставляете сервис на корневом уровне, Angular создает единственный, общий экземпляр HeroService и внедряет его в любой класс, который его запрашивает.

    Регистрация провайдера в метаданных @Injectable() также позволяет Angular оптимизировать приложение удаляя сервис из скомпилированного приложения, если он не используется, процесс, известный как tree-shaking.

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

    Для регистрации на этом уровне используйте свойство providers декоратора @NgModule().

    1
    2
    3
    4
    5
    6
    7
    @NgModule({
        providers: [
            BackendService,
            Logger,
        ],
        // …
    })
    
  • Когда вы регистрируете провайдера на уровне компонента, вы получаете новый экземпляр сервиса с каждым новым экземпляром этого компонента.

    На уровне компонента зарегистрируйте поставщика услуг в свойстве providers метаданных @Component().

    1
    2
    3
    4
    5
    @Component({
        selector:    'app-hero-list',
        templateUrl: './hero-list.component.html',
        providers:  [ HeroService ]
    })
    

Для получения более подробной информации смотрите раздел Dependency Injection.

Ссылки

Комментарии