@Injectable() decorator and providers array

前端 未结 2 1235
暗喜
暗喜 2021-01-17 04:38

Does a service that is provided in \"root\" within the @Injectable() decorator still have to be in the providers array of the module?

The Angular documentation doesn

2条回答
  •  小鲜肉
    小鲜肉 (楼主)
    2021-01-17 05:10

    The bullet points in the link you provided are all different methods of registering services, from the least specific to most specific.

    App-specific - use @Injectable({ providedIn: 'root' })

    When you provide the service at the root level, Angular creates a single, shared instance of HeroService and injects it into any class that asks for it. Registering the provider in the @Injectable() metadata also allows Angular to optimize an app by removing the service from the compiled app if it isn't used.

    Module-specific - register in module providers

    When you register a provider with a specific NgModule, the same instance of a service is available to all components in that NgModule. To register at this level, use the providers property of the @NgModule() decorator,

    Component-specific - register in component

    When you register a provider at the component level, you get a new instance of the service with each new instance of that component. At the component level, register a service provider in the providers property of the @Component() metadata.

    All quotes above are from the official Introduction to services and dependency injection page

    • If you only have one module, then the first two methods are equivalent and only need to use one method. It's easier to go with @Injectable - the default CLI approach.
    • If you want to share service instances between multiple modules, use the first method.
    • If you want one instance per independent module, use the second approach.
    • If you want to share an app-wide instance with all components except one, then use the first approach in addition to the third approach for the one anomalous component.

    It is my opinion that most use cases would fall into the first two approaches.

    Registering module-specific services

    Tip: just use providedIn: 'root'. Unused services won't be compiled for a module if it isn't used due to tree shaking. Declaring module-specific services seems redundant and, as we shall see, can cause problems.

    There are two ways to register module-specific services - either from the module or from the service.

    module

    @NgModule({
      providers: [MyService]
    })
    export class MyModule {}
    

    service

    @Injectable({ providedIn: MyModule })
    

    The latter is the officially recommended approach. Declaring the providers array is a hangover from the earlier days.

    From the docs:

    The example above shows the preferred way to provide a service in a module. This method is preferred because it enables tree-shaking of the service if nothing injects it. If it's not possible to specify in the service which module should provide it, you can also declare a provider for the service within the module

    Why you should just use providedIn: 'root'

    So we see that this approach is tree-shakeable. So far so good. But you will end up with circular references if you simply try to import the same module that the components consuming the clients are declared in.

    Take this setup:

    my-module

    declarations: [
      MyComponent
    ]
    

    my-service

    @Injectable({ providedIn: MyModule })
    

    my-component

    constructor(private myService: MyService) {}
    
    • my-service imports my-module
    • my-module imports my-component
    • my-component imports my-service

    There is a circular dependency.

    The workaround for this is to create a service module and import that into your module.

    my-module

    imports: [
      MyModuleServices
    ],
    declarations: [
      MyComponent
    ]
    

    my-module-services

    
    

    my-service

    @Injectable({ providedIn: MyModuleServices })
    

    my-component

    constructor(private myService: MyService) {}
    

    This is a very long-winded alternative to simply using providedIn: 'root' and letting the tree shaking do the work.

提交回复
热议问题