services
currentcy 和 language,都有持久化。base site 没有。Service 里还包含通过 url 设置 context 的实现类。总共分 4 组实现。
base-site-initializer.ts(BaseSiteInitializer)
首先,这个类是 Injection Token APP_INITIALIZER
的 provider,在应用程序初始化时执行。
通过构造函数注入的参数,第一个参数来自 facade
文件夹下的 Service 实现,第二个来自 site-context 同级的 config
文件夹。
@Injectable({ providedIn: 'root' }) export class BaseSiteInitializer implements OnDestroy { constructor( protected baseSiteService: BaseSiteService, protected configInit: ConfigInitializerService ) {}
initialize
这个方法加载默认值,被 APP_INITIALIZER 的 provider 调用。
initialize(): void { this.subscription = this.configInit .getStable('context') .pipe( // TODO(#12351): <--- plug here explicitly SiteContextRoutesHandler switchMap(() => this.setFallbackValue()) ) .subscribe(); }
setFallbackValue
protected setFallbackValue(): Observable<unknown> { return this.configInit .getStable('context') .pipe( tap((config: SiteContextConfig) => this.setDefaultFromConfig(config)) ); }
这里的 this.configInit.getStable(‘context’) 留待将来研究。
setDefaultFromConfig
从 config 的默认值设置 Active Base Site:
protected setDefaultFromConfig(config: SiteContextConfig): void { if (!this.baseSiteService.isInitialized()) { this.baseSiteService.setActive( getContextParameterDefault(config, BASE_SITE_CONTEXT_ID) ); } }
我们可以从调试器里观察一下运行时的行为:
所有的 APP_INITIALIZER provider 被调用,包括本章节正在介绍的 BaseSiteInitializer 的 initialize 方法:
subscribe 最终导致 setDefaultFromConfig 被调用:
此时 context 的所有值都已经就位了。
currency-state-persistence.service.ts (CurrencyStatePersistenceService)
注入的三个依赖:
export class CurrencyStatePersistenceService { constructor( protected statePersistenceService: StatePersistenceService, protected currencyService: CurrencyService, protected config: SiteContextConfig ) {}
(1) 来自 state 文件夹
(2) 来自 site-context facade 文件夹下
(3) 就是一个 abstract class,包含 urlParameters 属性,类型为 string[]
, 以及 [contextName: string]: string[]
initSync
这个方法调用了 state 文件夹下的 StatePersistenceService,传递 key,state$ 和 onRead 三个参数。
关于 CurrencyService 的 isInitialized 方法,我加了 config.log
, 两次打印都是 true:
onRead
成功从 localstorage 里读取到了 USD,但是没有进入代码 29 行的 IF 分支,∵ currencyService.isInitialized 返回了 true:
所以这里没有执行 setActive 方法。setActive 方法是通过 url 解析而触发调用的:
site-context-params.service.ts (SiteContextParamsService)
该类通过构造函数注入的三个依赖,都是 site-context
文件夹内实现的资源。
SiteContextConfig 是在全局 config 对象上新增的 context 字段:
export abstract class SiteContextConfig { context?: { urlParameters?: string[]; [contextName: string]: string[] | undefined; }; }
getContextParameters
该方法获得 context 里除了 urlParameters 之外的其他 context parameter,比如 language 和 currency 的字段名称。
getContextParameters(): string[] { if (this.config.context) { return Object.keys(this.config.context).filter( (param) => param !== 'urlParameters' ); } return []; }
getUrlEncodingParameters
取得 context 中的 url parameter 值:
getUrlEncodingParameters(): string[] { return (this.config.context && this.config.context.urlParameters) || []; }
getParamValues
取得参数的值内容:
getParamValues(param: string): string[] { return getContextParameterValues(this.config, param); }
getSiteContextService
根据参数名称,手动取得注入的参数服务类实例。在运行时,从浏览器地址栏 url 里,提取出地址栏包含的 url 参数:
解析出的 JSON 对象:
针对每个参数对象,根据参数名称,解析出对应的参数 service class,再调用 setValue 方法。
从 service map 里获得 service class 的 type,然后使用 Angular injector
进行实例注入:
将 active base site 设置成浏览器地址栏 url 里包含的值:
这个 base-site.service.ts 位于 facade 层:
另外,当 context 发生变化时,service class 的 getActive 也会发射最新的数据:
举个例子,从下拉列表里更改语言:
于是最新的语言,就被传入 subscribe 指定的回调函数里: