1

我有一个大型项目,在 webpack、angular 4 下运行良好,最近转移到了 angular 5,以及改进的 webpack 配置。“开发”构建加载得很好。

但是“生产”构建加载并因“请添加@NgModule 注释”而死 - 哎呀,因为有这么多人在这个浅滩上坠毁。

因此,我关闭了 mangle 以查看导致问题的组件。我发现导入开头列出的任何内容:@NgModule 中的 [ ... ] 数组部分都已列出。为什么会这样?是什么导致了这个可爱的错误?

为什么总是找不到导入开头列出的第一个模块?

在下面列出的版本中,错误是:

Uncaught Error: Unexpected value 'FormsModule' imported by the module 'AppModule'. Please add a @NgModule annotation.

以下是我希望能有所启发的项目部分:

@NgModule({
    bootstrap: [ AppComponent ],
    declarations: COMPONENTS,
    imports: [ // import Angular's modules
      FormsModule,
      ReactiveFormsModule,
      HttpModule,
      BrowserModule,
      RouterModule.forRoot(ROUTES, { useHash: true, preloadingStrategy: PreloadAllModules }),
      BsDropdownModule.forRoot(),
      AlertModule.forRoot(),
      DatepickerModule.forRoot(),
      TooltipModule.forRoot(),
      ModalModule.forRoot(),
      LocalStorageModule.withConfig({
        prefix: 'tracker',
        storageType: 'localStorage'
      }),
      A2Edatetimepicker,
      DndModule.forRoot(),
      NgPipesModule,
      ColorPickerModule
    ],
    providers: [ // expose our Services and Providers into Angular's dependency injection
      ENV_PROVIDERS,
      APP_PROVIDERS,
      {
        provide: AuthHttp,
        useFactory: (http: Http, localStorage: LocalStorageService) => {
          let conf: AuthConfig;
          conf = new AuthConfig({
            tokenName: 'token',
            tokenGetter: (() => {
              const t = localStorage.get<any>('id_token');
              // console.log(t);
              return t;
            }),
            globalHeaders: [{'Content-Type': 'application/json'}],
          });
          return new AuthHttp(conf, http);
        },
        deps: [Http, LocalStorageService]
      },
      {
        provide: SocketService,
        useFactory: (broadcast: BroadCaster,
                     authHttp: AuthHttp) => {
          return new SocketService(broadcast, authHttp);
        },
        deps: [BroadCaster, AuthHttp]
      },
      {
        provide: ImportUserData,
        useFactory: (authHttp: AuthHttp,
                     localStorage: LocalStorageService) => {
          return new ImportUserData(authHttp, localStorage);
        },
        deps: [AuthHttp, LocalStorageService]
      },
      {
        provide: AuthService,
        useFactory: (http: Http,
                     authHttp: AuthHttp,
                     localStorage: LocalStorageService) => {
          const as = new AuthService(http, authHttp, localStorage);
          return as;
        },
        deps: [Http, AuthHttp, LocalStorageService]
      },
      {
        provide: UserCondition,
        useFactory: (authHttp: AuthHttp,
                     broadcast: BroadCaster,
                     localStorage: LocalStorageService,
                     sanitizer: DomSanitizer,
                     zone: NgZone) => {
          return new UserCondition(authHttp, broadcast, localStorage, sanitizer, zone);
        },
        deps: [AuthHttp, BroadCaster, LocalStorageService, DomSanitizer, NgZone]
      },
      {
        provide: ProfileComponent,
        useFactory: (userCondtion: UserCondition,
                     sanitizer: DomSanitizer,
                     auth: AuthService) => {
          return new ProfileComponent(userCondtion, sanitizer, auth);
        },
        deps: [UserCondition, DomSanitizer, AuthService]
      },
      {
        provide: LogoutWarningComponent,
        useFactory: (userCondtion: UserCondition,
                     broadcaster: BroadCaster,
                     versionS: Version,
                     auth: AuthService) => {
          return new LogoutWarningComponent(userCondtion, broadcaster, versionS, auth);
        },
        deps: [UserCondition, BroadCaster, Version, AuthService]
      },
    ]
  })
  export class AppModule implements OnDestroy {
    private mVersion: string;
    ...
    ...
  } 

更新此问题:

几天后,我取得了相当大的进步。
我还学到了一些可以帮助其他人的东西——遇到这种问题的人。

  1. 注释掉 webpack 构建的生产版本中几乎所有的插件。
  2. 关闭名称混乱。

完成此操作后,我了解到问题来自项目中设置配置部分的“生产”:angular-starter。问题出现的地方在“build-utils.js”文件中:

    const ts = require('typescript');
    const path = require('path');
    const fs = require('fs');
    const helpers = require('./helpers');

    const DEFAULT_METADATA = {
      title: 'Tracker',
      baseUrl: '/',
      isDevServer: helpers.isWebpackDevServer(),
      HMR: helpers.hasProcessFlag('hot'),
      AOT: process.env.BUILD_AOT || helpers.hasNpmFlag('aot'),
      E2E: !!process.env.BUILD_E2E,
      WATCH: helpers.hasProcessFlag('watch'),
      tsConfigPath: 'tsconfig.webpack.json',

      /**
       * This suffix is added to the environment.ts file, if not set the default environment file is loaded (development)
       * To disable environment files set this to null
       */
      envFileSuffix: ''
    };

    function supportES2015(tsConfigPath) {
      if (!supportES2015.hasOwnProperty('supportES2015')) {
        const tsTarget = readTsConfig(tsConfigPath).options.target;
        supportES2015['supportES2015'] = tsTarget !== ts.ScriptTarget.ES3 && tsTarget !== ts.ScriptTarget.ES5;
      }
      return supportES2015['supportES2015'];
    }

    function readTsConfig(tsConfigPath) {
      const configResult = ts.readConfigFile(tsConfigPath, ts.sys.readFile);
      return ts.parseJsonConfigFileContent(configResult.config, ts.sys,
        path.dirname(tsConfigPath), undefined, tsConfigPath);
    }

    function getEnvFile(suffix) {
      if (suffix && suffix[0] !== '.') {
        suffix = '.' + suffix;
      }

      if (suffix === null) {
        return;
      }

      let fileName = helpers.root(`src/environments/environment${suffix}.ts`);
      if (fs.existsSync(fileName)) {
        return fileName;
      } else if (fs.existsSync(fileName = helpers.root('src/environments/environment.ts'))) {
        console.warn(`Could not find environment file with suffix ${suffix}, loading default environment file`);
        return fileName;
      } else {
        throw new Error('Environment file not found.')
      }
    }

    /**
     * Read the tsconfig to determine if we should prefer ES2015 modules.
     * Load rxjs path aliases.
     * https://github.com/ReactiveX/rxjs/blob/master/doc/lettable-operators.md#build-and-treeshaking
     * @param supportES2015 Set to true when the output of typescript is >= ES6
     */
    function rxjsAlias(supportES2015) {
      try {
        const rxjsPathMappingImport = supportES2015 ? 'rxjs/_esm2015/path-mapping' : 'rxjs/_esm5/path-mapping';
        const rxPaths = require(rxjsPathMappingImport);
        return rxPaths(helpers.root('node_modules'));
      } catch (e) {
        return {};
      }
    }

    function ngcWebpackSetup(prod, metadata) {
      if (!metadata) {
        metadata = DEFAULT_METADATA;
      }

      // TURNED OFF THE buildOptimizer here:
      // const buildOptimizer = prod;
      const buildOptimizer = false;
      const sourceMap = true; // TODO: apply based on tsconfig value?
      const ngcWebpackPluginOptions = {
        skipCodeGeneration: !metadata.AOT,
        sourceMap
      };

      const environment = getEnvFile(metadata.envFileSuffix);
      // console.log('environment:', environment);
      if (environment) {
        ngcWebpackPluginOptions.hostReplacementPaths = {
          [helpers.root('src/environments/environment.ts')]: environment
        }
      }

      if (!prod && metadata.WATCH) {
        // Force commonjs module format for TS on dev watch builds.
        ngcWebpackPluginOptions.compilerOptions = {
          module: 'commonjs'
        };
      }

      const buildOptimizerLoader = {
        loader: '@angular-devkit/build-optimizer/webpack-loader',
        options: {
          sourceMap
        }
      };

      const loaders = [
        {
          test: /(?:\.ngfactory\.js|\.ngstyle\.js|\.ts)$/,
          use: metadata.AOT && buildOptimizer ? [ buildOptimizerLoader, '@ngtools/webpack' ] : [ '@ngtools/webpack' ]
        },
        ...buildOptimizer
          ? [ { test: /\.js$/, use: [ buildOptimizerLoader ] } ]
          : []
      ];
      console.log('loaders:', loaders);
      // console.log('ngcWebpackPluginOptions:', ngcWebpackPluginOptions);

      return {
        loaders,
        plugin: ngcWebpackPluginOptions
      };
    }


    exports.DEFAULT_METADATA = DEFAULT_METADATA;
    exports.supportES2015 = supportES2015;
    exports.readTsConfig = readTsConfig;
    exports.getEnvFile = getEnvFile;
    exports.rxjsAlias = rxjsAlias;
    exports.ngcWebpackSetup = ngcWebpackSetup;

在这里,我需要在上面的 ngcWebpackSetup() 函数中设置:'buildOptimizer' 为 false。缺点是最小化的代码从 3.59 MB 增长到 4.04 MB。但是代码现在可以在浏览器中正常加载和运行!

我希望能了解这里的问题并报告回来,并可能对“angular-starter”项目进行更正。

4

0 回答 0