首页 > 解决方案 > Angular 12 IVY angular-universal AppServerModuleNgFactory 不存在

问题描述

早上好,首先,对不起我糟糕的英语,但我需要帮助。

情况:Angular 12 + Universal SSR 我从 Angular 11 View Engine(完美运行)更新到 Angular 12,我想使用 SSR 的 Ivy。

问题:我们有多个网站的 1 个 SSR,在不同的项目中,并且运行 PM2 服务器。

如果我在 SSR 项目 AppServerModuleNgFactory 中使用,我会收到该消息

TypeError:无法读取未定义的属性“moduleType”

在预览 Angular 11 视图引擎 main.js 存在 AppServerModuleNgFactory 但在新的 Angular 12 Ivy main.js 不存在 AppServerModuleNgFactory

非常感谢

const { AppServerModuleNgFactory } = require(Path.resolve(
  process.cwd(),
  "lib/sites-ssr/main"
));
const fileLocation = Path.resolve(process.cwd(), "views") + "/index" + req.query.hashfile + ".html";
const indexHtml = readFileSync(fileLocation, "utf-8").toString();
const url = SITE_CONFIG.public_path + req.originalUrl.split("?")[0]";
renderModuleFactory(AppServerModuleNgFactory, {
            document: indexHtml,
            url: url,
          })
          .then((html) => {
             res.status(200).send(new SSRResponse({
                posts: global["posts_ssr"],
             },html));
          })
          .catch((err) => {
              console.log(err);
              res.sendStatus(500);
          });

但是,如果我在没有 NgFactory 的情况下使用 AppServerModule,我会收到该消息

TypeError:无法读取未定义的属性“名称”

我在控制台中打印的下一个对象

<ref *1> [class AppServerModule] { 'ɵfac': [Function: AppServerModule_Factory], 'ɵmod': { type: [Circular *1], bootstrap: [ [Function] ], declarations: [], imports: [] ,出口:[],transitiveCompileScopes:null,模式:null,id:null},'ɵinj':{提供者:[],进口:[[Array]]}}

这里的结构主要项目

-Project
-tsconfig.json
-package.json
-angular.json
-src
    -app
        -app.server.module.ts
-projects
    -projectName
        -main.server.ts
        -tsconfig.app.json
        -tsconfig.server.json
        -src
            -main.ts

文件:package.json

"scripts": {
    "build": "ng run projectName:build:production --optimization=true",
    "build-server": "ng run projectName:server:production"
}

角.json

"projectName": {
    "build": {
        "builder": "@angular-devkit/build-angular:browser",
        "options": {
            "outputPath": "dist/projectName",
            "index": "projects/projectName/src/index.html",
            "main": "projects/projectName/src/main.ts",
            "polyfills": "projects/projectName/src/polyfills.ts",
            "tsConfig": "projects/projectName/tsconfig.app.json",
            "aot": true,
          },
          "configurations": {
            "production": {
              "fileReplacements": [
                {
                  "replace": "projects/projectName/src/environments/environment.ts",
                  "with": "projects/projectName/src/environments/environment.prod.ts"
                }
              ],
              "optimization": true,
              "outputHashing": "none",
              "sourceMap": false,
              "namedChunks": false,
              "extractLicenses": true,
              "vendorChunk": false,
              "buildOptimizer": false,
              "aot": true,
              "serviceWorker": false,
            },
          },
          "defaultConfiguration": "production"
        },
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "configurations": {
            "production": {
              "browserTarget": "nameProject:build:production"
            },
            "development": {
              "browserTarget": "nameProject:build:development"
            }
          },
          "defaultConfiguration": "development"
        },
        "lint": {
          "builder": "@angular-devkit/build-angular:tslint",
          "options": {
            "tsConfig": [
              "projects/nameProject/tsconfig.app.json",
              "projects/nameProject/tsconfig.spec.json",
              "projects/nameProject/e2e/tsconfig.json"
            ],
            "exclude": ["**/node_modules/**"]
          }
        },
        "server": {
          "builder": "@angular-devkit/build-angular:server",
          "options": {
            "outputPath": "dist/server",
            "main": "./projects/nameProject/main.server.ts",
            "tsConfig": "./projects/nameProject/tsconfig.server.json",
            "bundleDependencies": false
          },
          "configurations": {
            "production": {
              "sourceMap": false,
              "optimization": true,
            },
          }
        },
      }
    }

构建 main.ts

import "zone.js";

enableProdMode();
document.addEventListener("DOMContentLoaded", () => {
  platformBrowserDynamic()
    .bootstrapModule(ProjectNameModule)
    .catch((err) => console.log(err));
});

构建 tsconfig.app.json

{
  "extends": "../../tsconfig.json",
  "compilerOptions": {
    "outDir": "../../out-tsc/app",
    "types": []
  },
  "angularCompilerOptions": {
    "enableIvy": true,
    "preserveWhitespaces": false,
    "genDir": "compiled",
    "skipMetadataEmit": true
  },
  "files": ["./src/main.ts", "./src/polyfills.ts"],
  "include": ["src/**/*.d.ts"]
}

构建 ../../tsconfig.json

{
  "compileOnSave": false,
  "compilerOptions": {
    "baseUrl": "./",
    "outDir": "./dist/out-tsc",
    "sourceMap": false,
    "declaration": false,
    "module": "es2020",
    "moduleResolution": "node",
    "target": "es2015",
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "importHelpers": true,
    "typeRoots": ["node_modules/@types"],
    "lib": ["es2018", "dom"],
    "types": ["node"],
  },
  "exclude": ["node_modules"],
  "files": [
    "./projects/nameProject/src/main.ts",
    "./projects/nameProject/main.server.ts",
    "./src/polyfills.ts",
    "./custom.d.ts",
    "./src/app/app.module.ts",
  ],
  "angularCompilerOptions": {
    "enableIvy": true
  }
}

服务器 main.server.ts

import "zone.js";

enableProdMode();
export { AppServerModule } from "../../src/app/app.server.module";
export { renderModule, renderModuleFactory } from "@angular/platform-server";

应用服务器模块

import {
  ServerModule,
  ServerTransferStateModule,
} from "@angular/platform-server";

import { AppComponent } from "./app.component";
import { NameProjectModule } from "../../projects/nameProject/src/app/nameProject.module";
import { NgModule } from "@angular/core";

    @NgModule({
  imports: [NameProjectModule, ServerModule, ServerTransferStateModule],
  bootstrap: [AppComponent],
  providers: [],
  exports: [],
    })
    export class AppServerModule {}

服务器 tsconfig.server.json

    {
  "extends": "../../tsconfig.json",
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "outDir": "../../out-tsc/app-server",
    "baseUrl": "./",
    // Set the module format to "commonjs":
    "module": "commonjs",
    "types": ["node"]
  },
  "files": [
    "main.server.ts",
    "server.ts"
  ],
  "exclude": ["test.ts", "**/*.spec.ts"],
  // Add "angularCompilerOptions" with the AppServerModule you wrote
  // set as the "entryModule".
  "angularCompilerOptions": {
    "entryModule": "src/app/app.server.module#AppServerModule",
    "enableIvy": true
  }
    }

结构项目SSR,弹出顺序为app.ts -> view-render.controller.ts -> server.ts -> env-selector.ts

-Project
    -lib
        -app.ts
        -server.ts
        -controllers
            -view-renderer
                -view-renderer.controller.ts
        -sites-ssr
            -environments
                env-selector.ts
            -main.js

应用程序.ts

    constructor() {
    this.app = express();
    this.config();
    this.app.use(express.json());
    this.routes.routes(this.app); // routes in view-render.controller.ts
    this.app.disable("view cache");
    }

视图-render.controller.ts

    const { AppServerModuleNgFactory } = require("../../sites-ssr/main");

    public routes(app) {
    this.app = app;
    app.route("*").post((req, res) => {
      this.proxyRequest(req, res);
    });
    }

    proxyRequest(req, res) {
      setEnv(this.app, req, res)
        .then((env) => {
          const fileLocation =
            Path.resolve(process.cwd(), "views") +
            "/index" +
            req.query.hashfile +
            ".html";

          const indexHtml = readFileSync(fileLocation, "utf-8").toString();
          const url = SITE_CONFIG.public_path + req.originalUrl.split("?")[0];

          res.render(fileLocation, { req }, function (err, html) {
            renderModuleFactory(AppServerModuleNgFactory, {
              document: html,
              url: url,
            })
              .then((html) => {
                  res.send(
                    new SSRResponse(
                      {
                        posts: global["posts_ssr"],
                      },
                      html
                    )
                  );
                }
              })
              .catch((err) => {
                console.log(err);
                res.sendStatus(500);
              });
          });
        })
        .catch((err) => {
          console.log(err);
        });
    }

标签: angularivyangular-universalviewengineangular12

解决方案


推荐阅读