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 当中的注入器层级关系分为

应用级的注入器  ==>  主组件注入器  ==>  子组件注入器

在此配置中,服务已注入到我们的应用程序根目录中NgModuleSimpleService,因此位于我们的根目录中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 有就直接用组件的,如果没有就用模块里面的
posted @ 2020-12-24 00:47  猫神甜辣酱  阅读(137)  评论(0编辑  收藏  举报