Angular 通用客户端似乎不是 运行

Angular Universal client side doesn't seem to be running

当运行我转换为服务器端呈现应用程序时,我的应用程序运行并呈现服务器端,但似乎没有客户端应用程序运行。页面加载,并为每个路由呈现,但没有(点击)工作或任何事件(仅 routerLink),也没有客户端控制台工作。以下是我的应用程序和服务器模块、应用程序和服务器电源、快速服务器和版本

app.module.ts

//libs

//import modules
import { BrowserModule, BrowserTransferStateModule } from '@angular/platform-browser';
import { NgModule, APP_INITIALIZER, PLATFORM_ID, APP_ID, Inject } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { ColorPickerModule } from 'ngx-color-picker';
import { SortablejsModule } from 'angular-sortablejs';
import { FroalaEditorModule, FroalaViewModule } from 'angular-froala-wysiwyg';
import { Ng2SearchPipeModule } from 'ng2-search-filter';
import { MomentModule } from 'angular2-moment';
import { FileUploadModule } from 'ng2-file-upload';
import { CustomFormsModule } from 'ng4-validators';
import { NgSelectModule } from '@ng-select/ng-select';
import { NgPipesModule } from 'angular-pipes';
import { NgxPaginationModule } from 'ngx-pagination';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { CgBusyModule } from 'angular-busy2';
import { BrowserCookiesModule } from '@ngx-utils/cookies/browser';
//import { PrebootModule } from "preboot";

//TODO will be fixed shortly
//import { TextareaAutosizeModule } from 'ngx-textarea-autosize';
import { Location, CommonModule, isPlatformBrowser } from '@angular/common';
import * as _ from 'lodash';

//pipes
import { AppPipes } from './pipes/pipes';
//Directives
import { AppDirectives } from './directives/directives';
//Components
import { AppComponents, ModalComponents } from './components/components';
import { AppComponent } from'./app.component';
//Services
import { AppResources } from './components/resources';
import { AppServices } from './services/services';
import { AppLoadService } from '../config/app.load.service';
//HTTP Interceptors
import { APIInterceptor } from './services/api.interceptor';
const interceptors = [
  { provide: HTTP_INTERCEPTORS, useClass: APIInterceptor, multi: true }
];

//States
import { AppRoutingModule } from './app-routing.module';

export function init_app(appLoadService: AppLoadService) {
  return () => appLoadService.load();
}

const IMPORTS =  [
  FormsModule,
  CommonModule,
  BrowserAnimationsModule,
  HttpClientModule,
  NgbModule.forRoot(),
  ColorPickerModule,
  SortablejsModule.forRoot({ animation: 150 }),
  FroalaEditorModule.forRoot(),
  FroalaViewModule.forRoot(),
  Ng2SearchPipeModule,
  MomentModule,
  FileUploadModule,
  AppRoutingModule,
  CustomFormsModule,
  NgSelectModule,
  NgPipesModule,
  NgxPaginationModule,
  CgBusyModule,
  BrowserCookiesModule.forRoot(),
  BrowserTransferStateModule,
  BrowserModule.withServerTransition({
    appId: 'my-application'
  }),
];


const DECLARATIONS = [
  AppPipes, 
  AppDirectives,
  AppComponents
];

const PROVIDERS = [
  AppResources,
  AppServices,
  interceptors,
  Location,
  AppLoadService,
  { provide: APP_INITIALIZER, useFactory: init_app, deps: [AppLoadService], multi: true },
];

@NgModule({
  declarations:  DECLARATIONS,
  imports: IMPORTS,
  providers: PROVIDERS,
  entryComponents: ModalComponents,
  bootstrap: [AppComponent]
})
export class AppModule { 
  constructor(
    @Inject(PLATFORM_ID) private platformId: Object,
    @Inject(APP_ID) private appId: string
  ) {
    const platform = isPlatformBrowser(platformId) ?  'in the browser' : 'on the server'; 
    console.log(`Running ${platform} with appId=${appId}`);
  }
}

app.server.module.ts

import { NgModule,  } from '@angular/core';
import { ServerModule, ServerTransferStateModule } from '@angular/platform-server';
import { BrowserModule } from '@angular/platform-browser';
import { ModuleMapLoaderModule } from '@nguniversal/module-map-ngfactory-loader';
import { ServerCookiesModule } from '@ngx-utils/cookies/server';

import { AppModule } from './app.module';
import { AppComponent } from './app.component';

@NgModule({
  imports: [
    ServerTransferStateModule,
    ModuleMapLoaderModule,
    ServerCookiesModule.forRoot(),
    AppModule,
    ServerModule
  ],
  bootstrap: [AppComponent]
})
export class AppServerModule { }

main.ts

import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';

if (environment.production) {
  enableProdMode();
}

document.addEventListener('DOMContentLoaded', () => {
  platformBrowserDynamic().bootstrapModule(AppModule).catch(err => console.log(err));
});

main.server.ts

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

快递-server.ts

import 'reflect-metadata';
import 'zone.js/dist/zone-node';
import { renderModuleFactory } from '@angular/platform-server'
import { enableProdMode } from '@angular/core'
import * as express from 'express';
import * as cookieParser from 'cookie-parser';
import * as _ from 'lodash';
import { join } from 'path';
import { readFileSync } from 'fs';
import routes from 'main.routes';

const domino = require('domino');
const fs = require('fs');
const path = require('path');
const http = require('http');
const https = require('https');
const dotenv = require('dotenv');
const compression = require('compression');
const fallback = require('express-history-api-fallback');
const git = require('git-rev');
const request = require('request');


function getMockMutationObserver() {
  return class {
    observe(node, options) {
    }
    disconnect() {
    }
    takeRecords() {
      return [];
    }
  };
}

enableProdMode();
dotenv.config();

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

const app = express();

// Express Engine
import { ngExpressEngine } from '@nguniversal/express-engine';
// Import module map for lazy loading
import { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader';

const template = readFileSync(join(DIST_FOLDER, 'browser', 'index.html'), 'utf8');
const win = domino.createWindow(template);

global['window'] = win;
global['document'] = win.document;
global['DOMTokenList'] = win.DOMTokenList;
global['Node'] = win.Node;
global['Text'] = win.Text;
global['HTMLElement'] = win.HTMLElement;
global['navigator'] = win.navigator;
global['MutationObserver'] = getMockMutationObserver();

const { AppServerModuleNgFactory, LAZY_MODULE_MAP, AppServerModule } = require('main.server');


app.engine('html', (_, options, callback) => {
  var fullUrl = options.req.protocol + '://' + options.req.get('host') + options.req.originalUrl;
  let engine = ngExpressEngine({
    bootstrap: AppServerModuleNgFactory,
    providers: [
      provideModuleMap(LAZY_MODULE_MAP),
       {
        provide: 'REQUEST', useValue: (options.req)
      },
      {
        provide: 'RESPONSE', useValue: (options.req.res)
      },{
        provide: 'serverUrl',
        useValue: fullUrl
      }
    ]
  });

  engine(_, options, callback);
});

app.set('view engine', 'html');
app.set('views', 'src')

app.use(cookieParser(process.env.COOKIE_TOKEN));
app.use(compression());
app.use('/images', express.static('images'));
app.use('/dist', express.static('dist'));
app.use(fallback('index.htm', { root: DIST_FOLDER }));

app.get('*.*', express.static(join(DIST_FOLDER, 'browser')));

_.each(_.toPairs(routes.config), (route) => {
  let method = route[0].split(' ')[0];
  let url = route[0].split(' ')[1];
  let func = routes[route[1]];

  app[method](url, func);
})

app.get('/*', (req, res) => {
  res.render('index', { req, res });
});


//SSL termination for heroku is handled by the load balancer
//but locally is handled by node
if(!process.env.APP_ENV || (process.env.APP_ENV === 'feature')){
  var credentials = {
    key: readFileSync(join(process.cwd(), 'ssl', 'certs', 'server', 'privkey.pem')),
    cert: readFileSync(join(process.cwd(), 'ssl', 'certs', 'server', 'fullchain.pem')),
    ca: readFileSync(join(process.cwd(), 'ssl', 'certs', 'ca', 'my-root-ca.crt.pem'))
  };
  var httpsServer = https.createServer(credentials, app);
  httpsServer.listen(process.env.PORT || 8080, () => {
    console.log('application started in dev mode');
  });
} else {
  var httpServer = http.createServer(app);
  httpServer.listen(process.env.PORT || 8080, () => {
    console.log('application started in prod mode');
  });
}

版本

原来我所基于的通用启动器之一有错字,但结果是我将我的渲染视图指向了错误的目录。这就解释了为什么服务器端正在渲染,但没有客户端被启动,因为我正在渲染的 index.html 是浏览器 dist(因此没有包含任何包)。更改的具体行是

app.set('view engine', 'html');
app.set('views', 'src')

应该是

app.set('view engine', 'html');
app.set('views', join(DIST_FOLDER, 'browser'))