Проблема с рендерингом на стороне сервера в angular: не может генерировать main.bundle.js - PullRequest
1 голос
/ 26 июня 2019

Я запустил свое приложение путем рендеринга на стороне клиента, и все было в порядке.Но теперь, когда я пытаюсь скомпилировать и обслуживать свое приложение с помощью рендеринга на стороне сервера, я столкнулся с некоторыми проблемами.

Первое: когда я запускаю команду npm run build:ssr && npm run serve:ssr, все приложение может компилироваться и запускаться, и я вижу на терминале сообщение об обслуживании этого приложения на localhost: 3000.Я собираюсь на localhost: 3000 и вижу это:

    Error: You must pass in a NgModule or NgModuleFactory to be 
   bootstrapped
    at View.engine (/home/ewelina/rohub-portal/portal- 
   angular/dist/server.js:196864:23)
    at View.render (/home/ewelina/rohub-portal/portal- 
   angular/dist/server.js:46196:8)
    at tryRender (/home/ewelina/rohub-portal/portal - 
   angular/dist/server.js:43892:10)
    at Function.render (/home/ewelina/rohub-portal/portal- 
   angular/dist/server.js:43844:3)
    at ServerResponse.render (/home/ewelina/rohub-portal/portal- 
   angular/dist/server.js:52737:7)
    at /home/ewelina/rohub-portal/portal-angular/dist/server.js:149:9
    at Layer.handle [as handle_request] (/home/ewelina/rohub- 
   portal/portal-angular/dist/server.js:45618:5)
    at next (/home/ewelina/rohub-portal/portal- 
   angular/dist/server.js:45366:13)
    at Route.dispatch (/home/ewelina/rohub-portal/portal- 
   angular/dist/server.js:45341:3)
    at Layer.handle [as handle_request] (/home/ewelina/rohub- 
   portal/portal-angular/dist/server.js:45618:5)

Я попытался узнать об этой проблеме, и мой вывод заключается в том, что мой angular universal не может генерировать main.bundle.js в папке dist / server.Я не вижу никаких файлов пакета, и я думаю, что это сообщение появляется, когда angular не может сгенерировать шаблон HTML, потому что не может найти AppServerModuleNgFactory на сгенерированном main.js с сервера dist.Это единственный файл, который я могу добавить в свой файл server.ts.

Мой вопрос: как мне создать правильный файл main.bundle.js в dist / server и запустить это приложение с помощью рендеринга на стороне сервера?

angular.json

{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
    "ng-universal-demo": {
      "root": "",
      "sourceRoot": "src",
      "projectType": "application",
      "prefix": "app",
      "schematics": {},
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "dist/browser",
            "index": "src/index.html",
            "main": "src/main.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "src/tsconfig.server.json",
            "assets": [
              "src/assets",
              "src/assets/favicon.ico"
            ],
            "styles": [
              "src/bootstrap.min.css",
              "src/styles.css",
              "src/assets/custom.css",
              "src/animate.min.css"
            ],
            "scripts": []
          },
          "configurations": {
            "production": {
              "optimization": true,
              "outputHashing": "all",
              "sourceMap": false,
              "extractCss": true,
              "namedChunks": false,
              "aot": true,
              "extractLicenses": true,
              "vendorChunk": false,
              "buildOptimizer": true
            }
          }
        },
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "options": {
            "browserTarget": "ng-universal-demo:build"
          },
          "configurations": {
            "production": {
              "browserTarget": "ng-universal-demo:build:production"
            }
          }
        },
        "server": {
          "builder": "@angular-devkit/build-angular:server",
          "options": {
            "outputPath": "dist-server",
            "main": "src/main.server.ts",
            "tsConfig": "src/tsconfig.server.json"
          }
        },
          "test": {
            "builder": "@angular-devkit/build-angular:karma",
            "options": {
              "main": "src/test.ts",
              "polyfills": "src/polyfills.ts",
              "tsConfig": "src/tsconfig.spec.json",
              "karmaConfig": "src/karma.conf.js",
              "styles": [
                "src/styles.scss",
                "src/theme.scss"
              ],
              "scripts": [
                "node_modules/marked/lib/marked.js"
              ],
              "assets": [
                "src/favicon.ico",
                "src/assets"
              ]
            }
          },
          "lint": {
            "builder": "@angular-devkit/build-angular:tslint",
            "options": {
              "tsConfig": [
                "src/tsconfig.app.json",
                "src/tsconfig.spec.json"
              ],
              "exclude": [
                "**/node_modules/**"
              ]
            }
          }
        }
      }
    }

  }
package.json
{
  "name": "ng-universal-demo",
  "version": "0.0.0",
  "license": "MIT",
  "repository": {
    "type": "git",
    "url": "https://github.com/angular/universal-starter.git"
  },
  "contributors": [
    "AngularClass <hello@angularclass.com>",
    "PatrickJS <patrick@angularclass.com>",
    "Jeff Whelpley <jeff@gethuman.com>",
    "Jeff Cross <crossj@google.com>",
    "Mark Pieszak <mpieszak84@gmail.com>",
    "Jason Jean <jasonjean1993@gmail.com>",
    "Fabian Wiles <fabian.wiles@gmail.com>"
  ],
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "build:client-and-server-bundles": "ng build --prod && ng build --prod  --output-hashing=none",
    "build:static": "npm run build:client-and-server-bundles && npm run webpack:server && npm run generate:static",
    "build:dynamic": "npm run build:client-and-server-bundles && npm run webpack:server",
    "generate:static": "cd dist && node prerender",
    "build:ssr": "npm run build:client-and-server-bundles && npm run webpack:server",
    "serve:ssr": "node dist/server",
    "webpack:server": "webpack --config webpack.server.config.js --progress --colors",
    "serve:static": "cd dist/browser && http-server",
    "serve:dynamic": "node dist/server"
  },
  "private": true,
  "dependencies": {
    "@agm/core": "^1.0.0-beta.5",
    "@angular-devkit/build-angular": "^0.10.7",
    "@angular/animations": "^6.1.10",
    "@angular/cdk": "^7.3.7",
    "@angular/common": "^6.1.10",
    "@angular/compiler": "^6.1.10",
    "@angular/core": "^6.1.10",
    "@angular/flex-layout": "^7.0.0-beta.24",
    "@angular/forms": "^6.1.10",
    "@angular/http": "^6.1.10",
    "@angular/platform-browser": "^6.1.10",
    "@angular/platform-browser-dynamic": "^6.1.10",
    "@angular/platform-server": "^6.1.10",
    "@angular/router": "^6.1.10",
    "@nguniversal/express-engine": "^6.1.0",
    "@nguniversal/module-map-ngfactory-loader": "^6.1.0",
    "@types/lodash": "^4.14.123",
    "angular-in-memory-web-api": "^0.8.0",
    "angular-tree-component": "8.0.0",
    "animate.css": "^3.7.0",
    "body-parser": "^1.18.3",
    "bootstrap": "^4.3.1",
    "cookie-parser": "^1.4.4",
    "core-js": "^2.6.5",
    "css-loader": "^1.0.1",
    "express": "^4.15.2",
    "font-awesome": "^4.7.0",
    "jquery": "^3.3.1",
    "mock-browser": "^0.92.14",
    "moment": "^2.24.0",
    "morgan": "^1.9.1",
    "mydatepicker": "^1.10.2",
    "ng2-cache": "^0.2.1",
    "ng2-date-picker": "0.0.0",
    "ng2-datetime-picker": "^0.9.10",
    "ng2-nouislider": "^1.7.13",
    "ngx-bootstrap": "^2.0.0-beta.9-1",
    "ngx-cookie-service": "^1.0.10",
    "nouislider": "^9.2.0",
    "popper.js": "^1.14.7",
    "postcss-loader": "^3.0.0",
    "primeng": "^5.2.5",
    "rxjs": "6.4.0",
    "rxjs-compat": "^6.4.0",
    "webpack-cli": "^3.3.5",
    "xml2js": "^0.4.19",
    "zone.js": "^0.8.29"
  },
  "devDependencies": {
    "@angular/cli": "^6.2.9",
    "@angular/compiler-cli": "^6.1.10",
    "@angular/language-service": "^6.1.10",
    "@nicky-lenaers/ngx-scroll-to": "^1.1.1",
    "@types/file-saver": "0.0.1",
    "@types/node": "^8.10.44",
    "codelyzer": "0.0.28",
    "cpy-cli": "^1.0.1",
    "file-saver": "^1.3.8",
    "http-server": "^0.10.0",
    "jasmine-core": "2.4.1",
    "jasmine-spec-reporter": "2.5.0",
    "karma": "1.2.0",
    "karma-chrome-launcher": "^2.2.0",
    "karma-cli": "^1.0.1",
    "karma-jasmine": "^1.1.2",
    "karma-remap-istanbul": "^0.2.2",
    "lodash": "^4.17.11",
    "ng2-datepicker": "^2.3.1",
    "ng2-slideable-directive": "1.0.13",
    "ng2-slider-component": "1.0.9",
    "ng2-styled-directive": "1.0.5",
    "protractor": "4.0.9",
    "reflect-metadata": "^0.1.10",
    "rserv": "^1.1.3",
    "ts-loader": "^4.0.0",
    "ts-node": "1.2.1",
    "tslint": "3.13.0",
    "typescript": "^2.9.2",
    "webpack": "^4.29.6",
    "webpack-dev-server": "^3.2.1"
  }
}
server.ts (piece of code)
const app = express();

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

const domino = require('domino');
const fs = require('fs');
const path = require('path');

const template = readFileSync(join(DIST_FOLDER, 'browser', 'index.html'))
    .toString();


const MockBrowser = require('mock-browser').mocks.MockBrowser;
const mock = new MockBrowser();
const win = domino.createWindow(template);
Object.assign(global, domino.impl);

global['Event'] = null;
global['KeyboardEvent'] = null;
global['Element'] = {};
global['Element'].prototype = {};
global['Element'].prototype.remove = undefined;

global['navigator'] = mock.getNavigator();
global['window'] = mock.getWindow();
global['document'] = mock.getDocument();
global['localStorage'] = global['window'].localStorage;

//here I add dist/server/mian because I hav an error that file doesn't exist during building
const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist/server/main');


import { ngExpressEngine } from '@nguniversal/express-engine';

import { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader';


app.engine('html', ngExpressEngine({
  bootstrap: AppServerModuleNgFactory,
  providers: [
    provideModuleMap(LAZY_MODULE_MAP)
  ]
}));

app.set('view engine', 'html');
app.set('views', join(DIST_FOLDER, 'browser'));
prerender.ts (piece of code)
import { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader';

//the same situation as on server.ts
const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist/server/main');
const PATHS = require('./static.paths');

const BROWSER_FOLDER = join(process.cwd(), 'browser');
const index = readFileSync(join('browser', 'index.html'), 'utf8');


PATHS.forEach(function (route) {
  chdir(BROWSER_FOLDER);

  route.split('/').filter(val => val !== '')
    .forEach(function (dir) {
      if (!existsSync(dir)) {
        mkdirSync(dir);
      }
      chdir(dir);
    });

  renderModuleFactory(AppServerModuleNgFactory, {
    document: index,
    url: route,
    extraProviders: [
      provideModuleMap(LAZY_MODULE_MAP)
    ]
  }).then(html => writeFileSync(join(BROWSER_FOLDER, route, 'index.html'), html));
});

1 Ответ

0 голосов
/ 27 июня 2019
 imports: [
      ScrollToModule.forRoot(),
      BrowserAnimationsModule,
      BrowserModule.withServerTransition({appId: 'ng-angular-portal'}),
      TreeModule,
      ButtonModule,
      MyDatePickerModule,
      BsDropdownModule,
      ButtonsModule,
      TypeaheadModule.forRoot(),
      PaginationModule.forRoot(),
      AlertModule,
      DatepickerModule,
      CollapseModule,
      AccordionModule,
      ReactiveFormsModule,
      BrowserTransferStateModule,
      FormsModule,
    ModuleMapLoaderModule,
      //ServerTransferStateModule,
      HttpClientModule,
      //ServerModule,
      RouterModule.forRoot(ROUTES),
    AgmCoreModule.forRoot({
      apiKey: "AIzaSyCiNA4333VvLGmOtKvbg7y7gZ9ohe81hNI",
    }),
  ],
.....

export class AppModule {
  constructor(private injector: Injector) {}

    public ngDoBootstrap() {
    if (isPlatformBrowser('my-app')) {
      const {createCustomElement} = require('@angular/elements');

        const AppElement = createCustomElement(AppComponent, {injector: this.injector });
        customElements.define('my-app', AppElement);
      }

    }


}
...