22.angular的一些零散知识(一)

1.组件生命周期、通信方式

angular中,一个组件暂时不再用到时,就会被销毁。当再次回到需要用到时,会重新生成一个新的实例,所以组件是有一定声明周期的。

  • ngOnchanges
  • ngInit
  • ngDoCheck
  • ngAfterContentInit
  • ngAfterContentChecked
  • ngAfterViewInit
  • ngAfterViewChecked
  • ngOnDestroy

ngOnchanges 除第一次外,还会在输入属性发生变化时执行
ngAfterContentInit、ngAfterContentChecked 发生在 ng-content 投射完毕之后
ngAfterViewInit、ngAfterViewChecked 发生在子组件渲染完毕之后
每次变更检测会依次执行 ngOnchanges (如果输入属性没有变化则不执行)、ngDoCheck、ngAfterContentChecked、ngAfterViewChecked

2.如何缓存组件

RouteReuseStrategy
angular中默认使用了一个路由策略的提供商RouteReuseStrategy,自行覆盖掉即可

// CustomReuseStrategy implements RouteReuseStrategy
providers: [
   { provide: RouteReuseStrategy, useClass: CustomReuseStrategy }
],
3.组件如何通信(待实践)

父子组件间

  • @Input、@Output
  • 获取对方实例(@ViewChild、@Inject)
  • RxJS Subject
  • RxJS 多播

兄弟组件间

  • 获取对方实例
  • RxJS Subject
  • RxJS 多播
4.如何动态创建、销毁组件
export class AboutComponent implements OnDestroy {
  @ViewChild('container', {read: ViewContainerRef, static: true}) container;
  componentRef: ComponentRef;
  factory: ComponentFactory;

  constructor(private resolver: ComponentFactoryResolver) {}
  
  create() {
    this.container.clear();
    this.factory = this.resolver.resolveComponentFactory(AboutChildComponent);
    this.componentRef = this.container.createComponent(this.factory);
  }

  ngOnDestroy(): void {
    this.componentRef.destroy();
  }
}
5.依赖注入

useClass 直接使用类生成实例作为依赖
useFactory 使用此工厂函数返回的实例作为依赖

@Component({
  providers: [
    {
      provide: MsgService,
      useFactory: (http, dep2, dep3) => {
        return new MsgService();
      },
      deps: [   // 可继续添加工厂函数的依赖
         Http, 
         new Inject(TheOpaqueToken),   // OpaqueToken需要手动调用new Inject()
         TheInjectionToken   // InjectionToken不需要手动调用new Inject()
      ]
    }
  ]
})
export class AboutComponent {
  constructor(private msg: MsgService) {}
}

useValue直接使用一个值作为依赖

  providers: [
    {
      provide: MsgService,
      useValue: 'hi there'
      // useValue: myConfig
    }
  ]

useExisting 直接使用已经存在的实例作为依赖,存在就不会再去new一个。

  providers: [
    {
      provide: MsgService,
      useExisting: MsgService
    }
  ]
token问题

token为非type时,如果直接使用字符串,容易造成冲突,特别是不清楚第三方模块细节时

const config = {};
{
   provide: 'myConfigToken', useValue: config
}

为避免这个问题,引入了OpaqueToken,用new OpaqueToken('myConfig')来作为token,其值是唯一的

const CONFIG_TOKEN = new OpaqueToken('config');
providers: [
     {
         provide: CONFIG_TOKEN,
         useValue: config
     }
 ]

但OpaqueToken在factoryProvider中,作为deps的话,需要手动调用new Inject()

const API_URL_TOKEN = new OpaqueToken('apiUrl');

providers: [
    {
        provide: DataService,
        useFactory: (http, apiUrl) => {
            // create data service
        },
        deps: [
            Http,
            new Inject(API_URL_TOKEN)
        ]
    }
]

所以最终又设计了InjectionToken,来改进这个问题。ng4之后,废除了OpaqueToken。


const API_URL = new InjectionToken('apiUrl');

providers: [
    {
        provide: DataService,
        useFactory: (apiUrl) => {
            // create data service
        },
        deps: [
            API_URL
        ]
    }
]
多级注入器

注入器分为模块注入器和组件注入器。组件和模块之间、父组件和子组件之间,形成了多级注入器。比如同一个token,MsgService,可能在Module、FatherComponent、ChildComponent都有提供商。那么选择哪一个呢,默认是从下往上查找提供商。

手动注入
除了使用new手动生成实例外,还有其他方式的手动注入方式。非type类型时就得手动注入。

使用Injector

export class AppComponent implements OnInit {
  constructor(private injector: Injector) {}
  ngOnInit(): void {
    const config = this.injector.get(CONFIG);
  }
}

使用属性装饰器@Inject

export class AppComponent {
  constructor( @Inject(CONFIG) private config: any) {}
}
关于依赖注入的几个属性装饰器

@Inject 注入依赖
@Optional 当注入器里面没有找到token对应的依赖的时候返回null
@Self 仅从当前组件注入器里面查找依赖
@SkipSelf 跳过当前注入器,直接从父级注入器开始查找
@Host 仅从宿主元素注入器里面查找依赖,用在指令中

Multi Providers
const SOME_TOKEN: InjectionToken> = new InjectionToken("SomeToken");

const injector = Injector.create([
  { provide: SOME_TOKEN, useValue: "dependency one", multi: true },
  { provide: SOME_TOKEN, useValue: "dependency two", multi: true }
]);
const dependencies = injector.get(SOME_TOKEN);
console.log(dependencies); // ['dependency one', 'dependency two']
内置的token常量

PLATFORM_INITIALIZER 平台初始化之后调用的回调函数
APP_BOOTSTRAP_LISTENER:每个启动组件启动完成之后调用的回调函数
APP_INITIALIZER 应用初始化之前调用的回调函数
HTTP_INTERCEPTORS http拦截

你可能感兴趣的:(22.angular的一些零散知识(一))