关于前端:Angular-InjectionToken-APPINITIALIZER-multi-标志位不同值的情况讨论

2次阅读

共计 2927 个字符,预计需要花费 8 分钟才能阅读完成。

下图是 SAP 电商云 Spartacus UI 中的一段和 Site Context 解决逻辑相干的代码:

export const contextServiceProviders: Provider[] = [
  BaseSiteService,
  LanguageService,
  CurrencyService,
  {
    provide: APP_INITIALIZER,
    useFactory: initializeContext,
    deps: [ConfigInitializerService, SiteContextRoutesHandler],
    multi: true,
  },
];

留神第 40 行的标记位,multi,默认值为 false,这意味着在代码后注册的 initializer 的实现,会笼罩掉先注册的 initializer provider 实现。换言之,这种状况下,APP_INITIALIZER 的 provider 只能有一个。

如果 multi: true 被设置,那么新的提供者会被增加到之前注册的提供者中,使得一个令牌的提供者不止一个。当这个令牌被调用时,angular 会执行所有这些令牌。

[外链图片转存失败, 源站可能有防盗链机制, 倡议将图片保留下来间接上传 (img-t9CQg0dA-1657529948839)(https://upload-images.jianshu…)]

因而,开发人员能够应用 multi: true 来创立 multi Provider 令牌。这意味着咱们能够创立多个函数 / 服务,并在初始化期间调用它。

做一个测试,别离创立两个 initializer 的实现:


export function initializeApp2() {return (): Promise<any> => {return new Promise((resolve, reject) => {console.log(`initializeApp2 called`);
      setTimeout(() => {console.log(`initializeApp2 Finished`);
        resolve();}, 2000);
    });
  };
}

而后应用 multi:true 给同一个 Injection Token APP_INITIALIZER 注册两个不同的 provider:

  providers: [ 
    AppInitService,
    {provide: APP_INITIALIZER,useFactory: initializeApp1, deps: [AppInitService], multi: true},
    {provide: APP_INITIALIZER,useFactory: initializeApp2, multi: true}
  ],

最初运行时,这两个 initializers 会同时失去触发。

另外,这个例子应用了 useFactory 为 Injection Token 注册 provider.

一个例子:

const WINDOW = new InjectionToken<Window>('A reference to the window object', {factory: () => window,
});

下面的例子,应用工厂函数作为提供者来设置 InjectionToken,就如同它是在应用程序的根注入器中显式定义的一样。当初咱们能够在应用程序的任意地位应用它:

@Component({selector: 'my-app'})
export class AppComponent {constructor(@Inject(WINDOW) window: Window) {}}

咱们能够应用 inject 函数,在工厂函数的执行环境里,来获取其余提供者的援用。让咱们看另一个例子:

import {inject, InjectionToken} from '@angular/core';
import {ActivatedRoute} from '@angular/router';

export type TimespanProvider = Observable<string>;

export const TIMESPAN = new InjectionToken('Subscribe to timespan query param', {factory() {const activatedRoute = inject(ActivatedRoute);

    return activatedRoute.queryParams.pipe(pluck('timespan'),
      filterNil(),
      distinctUntilChanged());
  },
});

下面的例子,咱们注入 ActivatedRoute,应用 inject 取得其 provider 实例,并为时间跨度查问参数返回一个 observable. 将 Observable<String> 导出成新的类型别名:TimespanProvider.

这个 Injection Token 的注入例子:

@Component({selector: 'app-home'})
export class HomeComponent implements OnInit {constructor(@Inject(TIMESPAN) private timespan$: TimespanProvider) {}

  ngOnInit() {this.timespan$.pipe(untilDestroyed(this)).subscribe(console.log);
  }
}

另一个例子:咱们有一个 ThemeService 实现类,将用户以后的 Theme 值裸露进来:

@Injectable({providedIn: 'root'})
export class ThemeService {private theme = new Subject<string>();
  theme$ = this.theme.asObservable();

  setTheme(theme: string) {this.theme.next(theme);
  }
}

Component 读取以后 Theme 的值:

@Component({
  selector: 'app-hello',
  template: `<h1>{{theme$ | async}}</h1>`
})
export class HelloComponent {
  theme$: Observable<string>;

  constructor(private themeService: ThemeService) {}

  ngOnInit() {this.theme$ = this.themeService.theme$;} 

}

采取 Injection Token 的实现形式:

export type ActiveThemeProvider = Observable<string>;
export const ACTIVE_THEME = new InjectionToken<ActiveThemeProvider>('Active theme', {factory() {return inject(ThemeService).theme$;
  }
});

首先定义一个新的 Type,而后应用 factory 办法,返回 Observable<string> 类型的后果。

正文完
 0