window 用作具有 Angular 4 AoT 的 useValue 提供程序时未定义

window is undefined when used as useValue provider with Angular 4 AoT

当Angular 4.0.2 应用程序提前编译时,提供程序定义为useValue

import { OpaqueToken, Provider } from '@angular/core';

export const windowToken = new OpaqueToken('window');
export const windowProvider = { provide: windowToken, useValue: window };

并像

一样使用
@NgModule({ providers: [windowProvider], ... })
export class AppModule {}

它编译正常,但在注入时 windowundefined

constructor(@Inject(windowToken) window) {
   window.navigator...
}

引导时抛出错误:

TypeError: Cannot read property 'navigator' of undefined

仔细查看自动生成的 app.module.ngfactory.js 似乎确实是 undefined:

...
import * as import39 from './window';
var AppModuleInjector = (function (_super) {
    ...
    AppModuleInjector.prototype.createInternal = function () {
        ...
        this._windowToken_26 = undefined;
        this._SomeService_27 = new import16.SomeService(this._windowToken_26);
    }
    AppModuleInjector.prototype.getInternal = function (token, notFoundResult) {
        ...
        if ((token === import39.windowToken)) {
            return this._windowToken_26;
        }
        ...

当使用与useFactory相同的服务时,一切正常:

export function windowFactory() {
  return window;
}
export const windowProvider = { provide: windowToken, useFactory: windowFactory };

这里使用 window 作为 useValue 提供商到底有什么问题?这是一个已知的陷阱吗?此限制是否适用于所有全局变量或所有 useValue 提供程序?

我遇到了类似的问题,但它与 SignalrWindow 有关。尽管概念和错误是相同的。

然后我在这里找到这篇文章(https://blog.sstorie.com/integrating-angular-2-and-signalr-part-2-of-2/),文章底部有一些评论帮助我解决了问题。

基本上,它归结为在提供者中使用工厂方法而不是 useValue。我不确定为什么会出现问题,但我知道这种方法可以解决 aot 问题。

修复步骤:

创建导出的函数

export function windowFactory(): any {
    return window;
}

然后在核心模块中,在 @NgModule 提供程序中你可以这样做:

...
providers: [
    { provide: SignalrWindow, useFactory: windowFactory }
  ]
...

基本上,您可以根据自己的喜好重命名这些方法(因此,在您的示例中它将是:)

export const windowProvider = { provide: windowToken, useFactory: windowFactory };

在 AoT 编译期间,angular CLI 静态分析代码也会生成 ngmodule.factory 文件。它看到 "useValue" 并检查静态值是否可用并将其放入 ngmodule.factory。在编译期间,这个值不可用,所以它保留了那个提供者值,因此,当注入构造函数时,它被 returned 为 "undefined"。

但是,提供 "useFactory" 的目的完全相同,在 运行 之前您不知道自己的价值。

因此,useValue 在您的方案中不起作用,但 "useFactory" 会起作用。

底线:当 AOT 编译时,只有当你有常量字符串或数字等静态值时才使用 "useValue"。否则使用 "useFactory" 到 return 运行-时间计算 object/values。