Angular Service
在Angular中,Service
是一个可注入的类,用于封装可重用的功能。Service
可以在任何Component
中使用,也可以用于其他Service
。
Service可以完成以下工作,很多和UI无关的操作都可以用Service来完成,这样可以保持Component
的简洁。
- 调用API获取后台数据 - API Service
- 验证用户输入 - Form Validation
- 日志服务 - Logging Service
- 数据库操作 - Database Service
创建Service
使用Angular CLI创建Service,下面的CLI命令生成一个OrderService
1 | ng generate service order # or, ng g s order |
该命令会在项目根目录下生成一个order.service.ts
文件,我们在其中添加一个getOrder
方法,用于获取订单信息。
1 | // order.service.ts |
Order类型定义如下
1 | // order.ts |
可以看到,OrderService
对应的Class是用@Injectable
装饰器修饰的,这样Angular就可以将其注入到Component
中。我们还给@Injectable
传递了一个参数providedIn: 'root'
,这表示该Service是一个全局Service,可以在整个Application中使用。
使用Service
通过构造函数注入Service
在ProductComponent
中使用OrderService
,我们需要在ProductComponent
的构造函数中注入OrderService
,然后调用OrderService
的方法。(注意,由于OrderService是providedIn: 'root'
的,所以使用者不需要在providers
数组中声明它)
1 | import {OrderService} from "../order.service"; |
通过inject
函数注入Service
Angular 14
引入了一个新的函数inject
, 可以用来注入Service,如下: 使用这种方式,我们不必再依赖构造函数的参数,可以在任何地方注入Service。
1 | import {OrderService} from "../order.service"; |
两种注入方式的区别
那么使用inject
函数注入比使用constructor
注入有何好处呢?主要体现在继承时,假设我有一个BaseComponent
,其构造函数中注入了某个service, 另外一个组件ProductComponent
继承自BaseComponent
,则ProductComponent
的构造函数也需要注入这个service
才能调用super
方法。
1 | // base.component.ts |
1 | // product.component.ts |
而使用inject
函数注入则不需要。父组件的service会自动注入到子组件中。
1 | // base.component.ts |
1 | // product.component.ts |
providedIn vs providers
在Angular中,我们可以使用providedIn
或者providers
来指定Service的提供范围。providedIn
是Angular 6中引入的新特性,用于替代providers
。
如果在定义Service时指定了providedIn: 'root'
,那么Angular会在应用启动时自动将该Service注入到根模块中,这样就可以在整个应用中使用该Service。在使用该Service的Component中,就不必再在providers
中声明该Service。
如果定义Service时没有指定providedIn
,那么就需要在使用该Service的Component中的providers
中声明该Service。
1 | ({ |
当Angular找不到一个Service的提供者时,会抛出一个错误,相信大家都见过这个错误,从下面的错误可知,_OrderService
没有提供者。
1 | ERROR Error [NullInjectorError]: R3InjectorError(Environment Injector)[_OrderService -> _OrderService]: |
更进一步,我们可以将Service的使用范围限定在某个Component中,这样其他Component就无法使用该Service。
1 | // order.service.ts |
注意,以上代码无法在应用启动时自动注入Service,使用者仍然需要在providers
中声明该Service。
1 | // product.component.ts |
如果在其他Component中尝试使用该Service,会抛出一个错误,如下:
1 | ERROR TypeError: Cannot read properties of undefined (reading 'provide') |
Service的Scope
providedIn: ‘root’
providedIn: 'root'
- 表示Service是一个全局Service,可以在整个应用中使用。Angular会在应用启动时自动将该Service注入到根模块中。
providedIn: ‘platform’
providedIn: 'platform'
- 表示Service是一个平台Service,这种服务可以跨越多个Angular应用实例共享同一个实例,只要这些应用实例运行在同一页面上。这个主要在微前端项目中使用,单体Angular应用用不到。
providedIn: ‘any’
providedIn: 'any'
- 这种方式下,每个lazy load的module都会有一个独立的Service实例。而所有的eager load的module共享一个Service实例。