Angular-universal 出现错误:您必须传入 NgModule 或 NgModuleFactory 才能被引导

Posted

技术标签:

【中文标题】Angular-universal 出现错误:您必须传入 NgModule 或 NgModuleFactory 才能被引导【英文标题】:Angular-universal getting error: You must pass in a NgModule or NgModuleFactory to be bootstrapped 【发布时间】:2018-09-25 18:39:30 【问题描述】:

我按照this guide 将现有的 angular-cli 应用程序转换为 angular-universal。

你可以看我的完整源代码here。

我可以同时构建浏览器和客户端项目,但是当我在浏览器中查看应用程序时出现以下错误:

错误:你必须传入一个 NgModule 或 NgModuleFactory 才能被 自举 在 View.engine (D:\ng-s-s-r-demo\dist\server.js:359545:23)

问题出在我的 server.ts 文件中,其中 AppServerModuleNgFactory 未定义,并且由于该工厂用于在 express 后端引导应用程序,因此引导失败.

./server.ts:

const MockBrowser = require('mock-browser').mocks.MockBrowser;
const mock = new MockBrowser();

// Faster server renders w/ Prod mode (dev mode never needed)
enableProdMode();

// Express server
const app = express();

const PORT = process.env.PORT || 4000;
const DIST_FOLDER = join(process.cwd(), 'dist');

// Fix for window error:
const domino = require('domino');
const fs = require('fs');
const path = require('path');
const template = fs.readFileSync(path.resolve('./', 'dist', 'browser/', 'index.html')).toString();
const win = domino.createWindow(template);

// workaround for leaflet
global['window'] = win;
global['document'] = win.document;

// workaround for nex-charts
win.screen =  deviceXDPI: 0, logicalXDPI: 0 ;
global['MouseEvent'] = win.MouseEvent;
global['navigator'] = mock.getNavigator();


// * NOTE :: leave this as require() since this file is built Dynamically from webpack
const  AppServerModuleNgFactory, LAZY_MODULE_MAP  = require('./dist/server/main.bundle');

// AppServerModuleNgFactory is undefined
console.log('AppServerModuleNgFactory', AppServerModuleNgFactory);

// This is injected
console.log('LAZY_MODULE_MAP', LAZY_MODULE_MAP);

// Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
app.engine('html', ngExpressEngine(
  bootstrap: AppServerModuleNgFactory,
  providers: [
    provideModuleMap(LAZY_MODULE_MAP)
  ]
));

./webpack.server.config.js:

module.exports = 
  entry: 
    // This is our Express server for Dynamic universal
    server: './server.ts',
    // This is an example of Static prerendering (generative)
    prerender: './prerender.ts'
  ,
  target: 'node',
  resolve:  extensions: ['.ts', '.js'] ,
  // Make sure we include all node_modules etc
  externals: [/node_modules/],
  output:  path: path.join(__dirname, 'dist'), filename: '[name].js' ,
  module:  rules: [ test: /\.ts$/, loader: 'ts-loader'] ,
  plugins: [
    new webpack.ContextReplacementPlugin(
      // fixes WARNING Critical dependency: the request of a dependency is an expression
      /(.+)?angular(\\|\/)core(.+)?/,
      path.join(__dirname, 'src'), // location of your src
       // a map of your routes
    ),
    new webpack.ContextReplacementPlugin(
      // fixes WARNING Critical dependency: the request of a dependency is an expression
      /(.+)?express(\\|\/)(.+)?/,
      path.join(__dirname, 'src'), 
    )
  ]

./src/tsconfig.server.json:


  "extends": "../tsconfig.json",
  "compilerOptions": 
    "outDir": "../out-tsc/app",
    "module": "commonjs",
    "baseUrl": "./",
    "types": ["node"],
    "typeRoots": ["../node_modules/@types"],
    "paths": 
      "@angular/*": [
        "../node_modules/@angular/*"
      ],
      "@nebular/*": [
        "../node_modules/@nebular/*"
      ]
    
  ,
  "exclude": [
    "test.ts",
    "**/*.spec.ts"
  ],
  "angularCompilerOptions": 
    "entryModule": "app/app.server.module#AppServerModule",
    "platform": 1
  

./src/main.server.ts:

export  AppServerModule  from './app/app.server.module';

./src/app/app.module.ts:

@NgModule(
  declarations: [AppComponent],
  imports: [
    BrowserModule.withServerTransition(appId: 'my-app'),
    BrowserAnimationsModule,
    HttpModule,
    AppRoutingModule,

    NgbModule.forRoot(),
    ThemeModule.forRoot(),
    CoreModule.forRoot(),
    environment.production ? ServiceWorkerModule.register('./ngsw-worker.js') : [],
  ],
  bootstrap: [AppComponent],
  providers: [
     provide: APP_BASE_HREF, useValue: '/' , WebWorkerService,
  ],
)
export class AppModule 

./src/app/app.server.module.ts:

@NgModule(
  imports: [
    AppModule,
    ServerModule,
    ModuleMapLoaderModule
  ],
  bootstrap: [AppComponent],
)
export class AppServerModule 

【问题讨论】:

同样的问题,还没找到解决办法 你有没有试过运行node_modules/.bin/ngc来生成app.server.module.ngfactory.ts? 这似乎更像是一个构建问题。如果可以通过简单的文本搜索找到ngModuleFactory,请检查生成的服务器文件main.bundle.js。如果没有,那就是构建问题。您可以尝试的命令是:ng build --prod --output-hashing=bundlesng build --prod --app 1 --output-hashing=false。然后是 webpack 命令:webpack --config webpack.server.config.js --show-error-details。但我认为存在一些 webpack 配置问题,并且它无法解析为正确的 server.ts 文件,因为可以使用选项 --show-error-details 【参考方案1】:

我检查了您的存储库,并且能够在浏览器中查看 dist 没有该错误。也许您在运行构建时忘记添加标志-prod?请试试这个

ng build --prod

您还可以完全删除 dist 和/或删除 node_modules,执行npm cache clean,运行npm install,然后再次尝试构建。

如果您使用的是 npm 脚本,我注意到您的 /server 构建缺少 --prod 标志。请试试这个

"build:server": "ng build --prod --app 1 --output-hashing=false",

【讨论】:

【参考方案2】:
    const  AppServerModuleNgFactory, LAZY_MODULE_MAP  = require('./dist/server/main.bundle');

您能否检查一下您的 main.bundle 中是否存在“AppServerModuleNgFactory”。它可能是其他名称,所以只需检查“ModuleNgFactory”找出哪个NgFactory正在导出您的应用程序,只需将其替换为工厂名称即可。

【讨论】:

尝试搜索但无法找到。角度版本是 12【参考方案3】:

mode: 'development', 用于webpack.config.js

【讨论】:

【参考方案4】:

ANGULAR 8 更新(v8.0.1 - 截至 2019 年 6 月)

对我来说,我使用 Angular CLI 运行了通用设置,但它并不能直接运行。经过几个小时的阅读,我发现这些包裹不匹配。我让 Angular 8 运行该项目,但我的 package.json 中的 nguniversal 包是在 v7 中指定的。

我建议将这些更新到您安装的相同版本的 Angular。默认情况下 CLI 确实应该这样做,但我猜(还没有?)。

对于 Angular 8,在撰写本文时(2019 年 6 月)这是版本 @next@8.0.0-rc.1,因此运行以下命令进行更新:

npm i --save @nguniversal/express-engine@next @nguniversal/module-map-ngfactory-loader@next

更新后,我仍然收到错误,并设法确定另一个问题。我还必须关闭服务器端应用程序的 Ivy 编译器。为此,我在tsconfig.server.json 中添加了以下行:


    "extends": "./tsconfig.app.json",
    ...
    "angularCompilerOptions": 
        ...
        "enableIvy": false
    
    ...

Ivy 在 Angular 8 中默认是关闭的,但是因为我的 tsconfig.server.json 扩展了 tsconfig.app.json,并且应用程序配置已打开 Ivy,所以我必须为服务器配置显式关闭它。

在这一切之后,服务器对内容的请求实际上开始为我工作了。

如果这对您没有帮助,我建议下载 angular 文档中提到的通用示例项目:

下载:https://angular.io/generated/zips/universal/universal.zip 文档:https://angular.io/guide/universal

下载后,比较所有相关文件以确保您拥有相同的文件。如果您在自己的项目中仍然遇到错误,但示例正在运行,那么请尝试将您的设置文件、模块和组件等一一移动到示例项目中,看看是什么破坏了它。这就是我能够确定是我的tsconfig.server.json 文件破坏了它的方法。

【讨论】:

谢谢! tsconfig.server.json 中的 "enableIvy": false 为我修复了运行 Angular v8.2.0、nguniversal/express-engine 和 nguniversal/module-map-ngfactory-loader v8.1.1【参考方案5】:

Angular Ivy 改变了server.ts 执行引导的方式。基本上,你必须运行

ng add @nguniversal/express-engine

然后将main.server.tsserver.ts/main.tstsconfig.server.jsonapp.server.module.ts中的旧s-s-r相关代码调整为新生成的server.ts。很可能创建的server.ts 将被放置在不同的路径中,因此不要忘记在angular.json 中更改servermain 路径。

【讨论】:

以上是关于Angular-universal 出现错误:您必须传入 NgModule 或 NgModuleFactory 才能被引导的主要内容,如果未能解决你的问题,请参考以下文章

Angular-universal - 生产问题

包天下加盟餐饮创业您必看的加盟攻略

Angular5 webpack ReferenceError:未定义窗口

Angular Universal:未定义导航器

Paypal:使用沙盒帐户进行测试要求重定向后登录

在 Angular 6 中将 CORS 标头添加到响应中