我有一个大型项目,在 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;
...
...
}
更新此问题:
几天后,我取得了相当大的进步。
我还学到了一些可以帮助其他人的东西——遇到这种问题的人。
- 注释掉 webpack 构建的生产版本中几乎所有的插件。
- 关闭名称混乱。
完成此操作后,我了解到问题来自项目中设置配置部分的“生产”: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”项目进行更正。