zoukankan      html  css  js  c++  java
  • 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-masterpackagescore est ender3providers_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 有就直接用组件的,如果没有就用模块里面的
    
  • 相关阅读:
    前端切图|点击按钮div变色
    当鼠标聚焦时输入框变色(focus事件实例)
    jedate-开始使用一款好用的时间插件
    jedate-开始使用一款好用的时间插件
    前端切图|点击按钮div变色
    当鼠标聚焦时输入框变色(focus事件实例)
    ajax实现简单的点击左侧菜单,右侧加载不同网页
    装饰者模式(Decorator、Compoment)(早餐销售装饰,动态添加职责)
    原型模式(Prototype)(对象、克隆广告邮件)
    hashcode
  • 原文地址:https://www.cnblogs.com/fangdongdemao/p/14182304.html
Copyright © 2011-2022 走看看