【问题标题】:@Injectable() decorator and providers array@Injectable() 装饰器和提供者数组
【发布时间】:2020-06-08 03:59:51
【问题描述】:

@Injectable() 装饰器中“根”中提供的服务是否仍必须位于模块的 providers 数组中?

Angular documentation 并没有真正给我答案或者我不太明白。

在我的核心文件夹中,我有一个在根目录中提供的身份验证服务。我不想在 app 模块中导入我的核心模块以使用提供的所有服务和组件。

我是否必须在模块的 providers 数组中额外设置服务,还是已经使用装饰器在根级别提供了它就足够了?

【问题讨论】:

    标签: angular dependency-injection angular-services angular-module


    【解决方案1】:

    您提供的链接中的要点是注册服务的所有不同方法,从最不具体到最具体。

    特定应用 - 使用 @Injectable({ providedIn: 'root' })

    当您在根级别提供服务时,Angular 会创建一个单独的、共享的 HeroService 实例,并将其注入到任何需要它的类中。在 @Injectable() 元数据中注册提供程序还允许 Angular 通过从已编译的应用程序中删除不使用的服务来优化应用程序。

    模块特定 - 在模块提供程序中注册

    当您向特定 NgModule 注册提供程序时,该 NgModule 中的所有组件都可以使用同一个服务实例。要在此级别注册,请使用 @NgModule() 装饰器的 providers 属性,

    组件特​​定-在组件中注册

    当您在组件级别注册提供程序时,您将获得一个新的服务实例以及该组件的每个新实例。在组件级别,在 @Component() 元数据的 providers 属性中注册一个服务提供者。

    以上所有引用均来自官方Introduction to services and dependency injection页面

    • 如果你只有一个模块,那么前两种方法是等价的,只需要使用一种方法。使用 @Injectable 更容易 - 默认的 CLI 方法。
    • 如果要在多个模块之间共享服务实例,请使用第一种方法。
    • 如果您希望每个独立模块一个实例,请使用第二种方法。
    • 如果您想与除一个组件之外的所有组件共享一个应用范围的实例,那么除了对一个异常组件使用第三种方法之外,还使用第一种方法。

    我认为大多数用例都属于前两种方法。

    注册特定模块的服务

    提示:只需使用providedIn: 'root'。如果由于摇晃树而未使用模块,则不会为模块编译未使用的服务。声明特定于模块的服务似乎是多余的,正如我们将看到的那样,可能会导致问题。

    有两种方法可以注册特定模块的服务 - 从模块或从服务。

    模块

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

    服务

    @Injectable({ providedIn: MyModule })
    

    后者是官方推荐的方法。声明提供者数组是早期的遗留物。

    来自the docs

    上面的示例显示了在模块中提供服务的首选方式。这种方法是首选的,因为如果没有任何注入它,它会启用服务的 tree-shaking。如果无法在服务中指定应由哪个模块提供,您也可以在模块中为服务声明提供者

    为什么你应该只使用providedIn: 'root'

    所以我们看到这种方法是可摇树的。到目前为止,一切都很好。但是,如果您只是尝试导入与声明使用客户端的组件相同的模块,那么您最终会得到循环引用。

    进行此设置:

    我的模块

    declarations: [
      MyComponent
    ]
    

    我的服务

    @Injectable({ providedIn: MyModule })
    

    我的组件

    constructor(private myService: MyService) {}
    
    • 我的服务导入我的模块
    • 我的模块导入我的组件
    • 我的组件导入我的服务

    存在循环依赖。

    解决方法是创建一个服务模块并将其导入到您的模块中。

    我的模块

    imports: [
      MyModuleServices
    ],
    declarations: [
      MyComponent
    ]
    

    我的模块服务

    
    

    我的服务

    @Injectable({ providedIn: MyModuleServices })
    

    我的组件

    constructor(private myService: MyService) {}
    

    这是一个非常冗长的替代方案,而不是简单地使用 providedIn: 'root' 并让摇树来完成这项工作。

    【讨论】:

    • 感谢您的详细回答。这绝对让我更清楚,更容易理解。但是,我还有一个问题。仍然可以像这样在@injectable() 装饰器中设置服务:@incectable ({ providedIn: 'SpecificComponentOrModule' })。这种方法是否等同于在模块的 providers 数组中设置服务的方法或任何特殊的方法?
    • 有趣。除了'root',我从未使用过providedIn。有趣的是,设置特定模块服务的官方指南是通过providedIn: MyModule,相当于将服务作为提供者添加到模块中。如果您尝试对组件执行此操作,您将获得循环引用。
    • 我已经在我的答案中附加了模块特定的服务详细信息。
    • 很好,谢谢。我认为这是对这个问题的完美回答。可能有几种方法应该仔细考虑。
    • 感谢您的深入解释。不过,我有一个我认为没有涵盖的案例:在延迟加载模块中定义的服务,其范围仅是模块。显然,使用providedIn: LazyLoadedModule 和一个注入了服务的组件会导致循环依赖,但我不喜欢让服务在根范围内可用的修复
    【解决方案2】:

    在@Injectable() 中的“根”中提供服务 装饰器还得在模块的 providers 数组中?

    不!

    当您使用 Angular CLI 创建服务时,我相信默认情况下会添加 providedIn: "root",因此从技术上讲,您不需要将自己的服务添加到提供程序数组中。

    我是否必须在 providers 数组中额外设置服务 模块,或者它已经在根级别提供就足够了 使用装饰器?

    使用装饰器在根级别提供就足够了。

    但是,并非我创建的所有服务都是全球性的。当我创建将在单个屏幕或子模块之间共享的服务时,将配置对象删除到@Injectable()metadata 对我来说并不少见——而不是在整个应用程序中。在这种情况下,我将不得不将该服务添加到使用它的各个组件或子模块声明的提供程序数组中。

    【讨论】:

      猜你喜欢
      • 2022-11-14
      • 1970-01-01
      • 1970-01-01
      • 2022-08-19
      • 2020-07-13
      • 2016-02-28
      • 2019-12-21
      • 2017-01-08
      • 2018-11-23
      相关资源
      最近更新 更多