我通过客户端渲染启动了我的应用程序,一切正常。但是现在,当我尝试通过服务器端渲染编译并为应用程序提供服务时,遇到了一些问题。
第一件事:运行命令时
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)
我试图了解此问题,结论是,我的通用角度无法在dist / server文件夹上生成main.bundle.js。我看不到任何捆绑文件,并且我认为,当angular无法生成html模板时,由于无法从dist服务器生成的main.js上找到AppServerModuleNgFactory,因此出现此通信。这是我可以添加到server.ts文件中的唯一文件。
我的问题是如何在dist / server上生成正确的main.bundle.js文件并通过服务器端渲染启动此应用?
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));
});
答案 0 :(得分:0)
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);
}
}
}