angular11源码探索八[服务三]
共享服务
@Injectable()
export class TestService {
constructor() { }
makeNoise() {
return '333';
}
}
使用
@Component({
selector: 'app-two',
templateUrl: './two.component.html',
styleUrls: ['./two.component.scss'],
providers: [TestService]
})
export class TwoComponent implements OnInit {
constructor(
private other: TestService
) {
console.log(other.makeNoise());
}
}
父组件注册TestService
子组件就不需要再次注册直接使用
子组件
@Component({
selector: 'app-a',
templateUrl: './a.component.html',
styleUrls: ['./a.component.scss'],
providers:[] // 无需再次注册
})
export class AComponent implements OnInit {
constructor(
private other: TestService
) {
console.log(other);
}
我们常见的要么直接依赖到
@Injectable({
providedIn: 'root',// 依赖根目录
})
但是我们不建议上面两种,我们需要在每一个模块的根模块注册
providers: [
TestService
]
注入器的层级关系
在程序启动的时候, Angular
首先会创建一个应用级注入器,然后将模块中声明的提供器,都注册到这个注入器当中,被注册的提供器除了应用的主模块声明的以外,还包括所有被引用的模块中声明的提供器,比如
// app.module.ts
@NgModule({
// ...
imports: [
BrowserModule,
FormsModule,
HttpModule
],
providers: [
// ...
]
})
这些模块当中声明的提供器都会被注册到应用级注入器当中,然后 Angular
会创建启动模块指定的主组件(bootstrap
指定的模块),不过在一般情况下 Angular
可以通过构造函数的参数自动注入所需的依赖
constructor(private http: Http) { }
Angular
当中的注入器层级关系分为
应用级的注入器 ==> 主组件注入器 ==> 子组件注入器
在此配置中,服务已注入到我们的应用程序根目录中NgModule
的 SimpleService
,因此位于我们的根目录中injector
。
因此,每个解析和注入令牌的请求SimpleService
都将转发到我们的单个根注入器。
因此,由于我们只有一个注入器来解决依赖关系,因此,每次我们请求将一个实例 SimpleService
注入到我们的组件之一中时,总是会注入 相同的实例。
请记住,如果我们从同一个注入器请求 同一个令牌, 我们将得到 同一个实例。
组件提供商
@Component({
selector: 'parent',
template: `...`,
providers: [ SimpleService ]
})
class ParentComponent {
constructor(private service: SimpleService) { }
}
现在每个人 ParentComponent
都有自己的子注入器并进行SimpleService
配置,如下所示
如果我们键入一个父组件,则只有该父组件及其子组件会自动更新
ParentComponent
现在的每个实例 都有其自己的实例 SimpleService
,因此状态不会全局共享,而仅在aParentComponent
及其子组件之间共享 。
请记住,当我们从不同的注入器请求 相同的令牌时,我们将获得 不同的实例。
如果我们希望每个组件具有一个服务实例,并与所有组件的子组件共享,则可以在组件装饰器的属性上对其进行配置。 providers
viewProviders
这个是@Component
特有的
向视图提供者注入类
组件的
export class TwoService {
}
@Component({
selector: 'app-two',
templateUrl: './two.component.html',
styleUrls: ['./two.component.scss'],
viewProviders: [TwoService]
})
constructor(
private two: TwoService
) {
虽然providers
好像也能实现这个功能,疑惑provider和viewProviders之间没有区别,那区别是viewProviders
不能使用<ng-content>
代码篇
angular-master\packages\core\test\render3\providers_spec.ts
abstract class Greeter {
abstract greet: string
}
const GREETER = new InjectionToken<Greeter>('greeter')
使用的时候添加到模块里
providers: [{provide: GREETER, useClass: GreeterClass}],
使用
constructor(
@Inject(GREETER) private test:GreeterClass
) {
console.log(test.makeNoise());
别名(对已有的进行重新命名查找)
providers: [
GreeterClass,
{provide: GREETER, useExisting: GreeterClass}
],
// 使用
constructor(@Inject('aaa') private aaa:GreeterClass) {
console.log(this.aaa);
}
函数形式
const GREETER = new InjectionToken<Greeter>('greeter');
abstract class Greeter {
abstract greet: string;
}
class GreeterClass implements Greeter {
greet = 'Class';
}
providers: [
GreeterClass, //第一种
{provide: GREETER, useFactory: () => new GreeterClass()} //第二种
]
这两个是等价的
使用
constructor(
// @Inject(GREETER) private aaa:GreeterClass,
private aaa:GreeterClass
) {
把服务useValue
通过参数传递到里面
const MESSAGE = new InjectionToken<string>('message');
const GREETER = new InjectionToken<Greeter>('greeter');
abstract class Greeter {
abstract greet: string;
}
class GreeterDeps implements Greeter {
constructor(public greet: string) {}
}
providers: [
{provide: MESSAGE, useValue: 'Message'},
{provide: GREETER, useClass: GreeterDeps, deps: [MESSAGE]}
],
使用
constructor(
@Inject(GREETER) private aaa:GreeterDeps
) {
console.log(aaa.greet) //Message
类似函数同理
providers: [
{provide: MESSAGE, useValue: 'Message'},
{provide: GREETER, useFactory: (msg: string) => new GreeterDeps(msg), deps: [MESSAGE]}
],
forwardRef 允许引用尚未定义的引用(但是不推荐项目中使用)
providers: [
forwardRef(()=>TwoService)
]
使用
constructor(
@Inject(TwoService) private two:TwoService
)
providers: [
{
provide: GREETER,
useValue: forwardRef(() => {
return {greet: 'Value'};
})
}
]
使用
constructor(
@Inject(GREETER) private aaa
) {
aaa()
{provide:'aaa',useClass:forwardRef(()=>TwoService)}
使用
@Inject('aaa') private two:TwoService
操作也可以使用的时候反向查询
@Inject(forwardRef(()=>'aaa')) private two:TwoService
别名
providers: [
TwoService,
{provide:'aaa',useExisting:forwardRef(()=>TwoService)}
]
覆盖策略
providers: [
{provide:'aaa',useValue:'Message 1'},
// 下面的会覆盖上面
{provide:'aaa',useValue:'Message 2'},
]
应该在组件中同时使用提供商和视图提供商
模块里面写了和组件里面写了
如果provide:string 是一致的,那么组件里面会覆盖模块里面的
组件中同时使用提供商和视图提供商
providers: [{provide: String, useValue: 'From providers'}],
viewProviders: [{provide: String, useValue: 'From viewProviders'}],
我们发现最终打印的视图提供商
'From viewProviders'
还有一种形式把两者都打印出来,给它们添加 multi:true
viewProviders: [{provide:'aaa',useValue:'From 1',multi: true}],
providers:[{provide:'aaa',useValue:'From 2',multi: true}],
// ["From 2", "From 1"]
遵循的规则是@Component 有就直接用组件的,如果没有就用模块里面的
决定自己的高度的是你的态度,而不是你的才能
记得我们是终身初学者和学习者
总有一天我也能成为大佬