在localhost模块上,延迟加载工作正常,在视图页面源中显示<app-root><app-root/>
中的元标记和HTML内容,但在实时服务器上没有显示。在实时服务器上,我只能看到AppModule
的直接子级组件的元标签和HTML内容,但懒惰加载的模块组件没有显示元标签和HTML。这种奇怪的行为只出现在实时服务器上。
app.server.module.ts
import { NgModule } from '@angular/core';
import { ServerModule } from '@angular/platform-server';
import { AppComponent } from './app.component';
import { AppModule } from './app.module';
@NgModule({
imports: [AppModule, ServerModule],
bootstrap: [AppComponent]
})
export class AppServerModule { }
main.server.ts
import '@angular/localize/init'
import '@angular/platform-server/init';
import { enableProdMode } from '@angular/core';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
export { AppServerModule } from './app/app.server.module';
export { renderModule } from '@angular/platform-server';
服务器.ts
import '@angular/localize/init';
import 'zone.js/dist/zone-node';
import 'reflect-metadata';
import { ngExpressEngine } from '@nguniversal/express-engine';
import * as express from 'express';
import { join } from 'path';
import { AppServerModule } from './src/main.server';
import { APP_BASE_HREF } from '@angular/common';
import { existsSync, readFileSync } from 'fs';
import { createWindow } from 'domino';
import 'localstorage-polyfill'
import 'localstorage-polyfill'
const scripts = readFileSync('dist/clientWeb/browser/index.html').toString();
const window = createWindow(scripts) as any;
(global as any).window = window;
(global as any).document = window.document;
(global as any).Event = window.Event;
(global as any).KeyboardEvent = window.KeyboardEvent;
(global as any).MouseEvent = window.MouseEvent;
// The Express app is exported so that it can be used by serverless Functions.
export function app(): express.Express {
const server = express();
const distFolder = join(process.cwd(), 'dist/clientWeb/browser');
const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';
// Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
server.engine('html', ngExpressEngine({
bootstrap: AppServerModule,
}));
server.set('view engine', 'html');
server.set('views', distFolder);
// Example Express Rest API endpoints
// server.get('/api/**', (req, res) => { });
// Serve static files from /browser
server.get('*.*', express.static(distFolder, {
maxAge: '1y'
}));
// All regular routes use the Universal engine
server.get('*', (req, res) => {
res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
});
return server;
}
function run(): void {
const port = process.env['PORT'] || 4000;
// Start up the Node server
const server = app();
server.listen(port, () => {
console.log(`Node Express server listening on http://localhost:${port}`);
});
}
const MockBrowser = require('mock-browser').mocks.MockBrowser;
const mock = new MockBrowser();
(global as any).localStorage = localStorage;
// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = mainModule && mainModule.filename || '';
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
run();
}
export * from './src/main.server';
应用程序模块.ts
import { BrowserModule, HammerGestureConfig, HAMMER_GESTURE_CONFIG, Title } from '@angular/platform-browser';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { AppLayoutComponent } from './shared/layout/app-layout/app-layout.component';
import { NgModule, CUSTOM_ELEMENTS_SCHEMA, ModuleWithProviders } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AppRoutingModule } from './app-routing.module';
import { HttpClientModule } from '@angular/common/http';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { Tradesmanapplayout2Component } from './shared/layout/tradesmanapplayout2/tradesmanapplayout2.component';
import { Supplierapplayout2Component } from './shared/layout/supplierapplayout2/supplierapplayout2.component';
import { Userapplayout2Component } from './shared/layout/userapplayout2/userapplayout2.component';
import { AppDasboardHeader2Component } from './shared/layout/app-dasboard-header2/app-dasboard-header2.component';
import { AppDasboardFooter2Component } from './shared/layout/app-dasboard-footer2/app-dasboard-footer2.component';
const routes: Routes = [
{
path: 'resetpassword',
component: AppHeaderLayoutComponent,
loadChildren: () => import('./common/resetPassword/resetpassword.module').then(m => m.ResetpasswordModule)
},
{
path: 'Supplier',
component: Userapplayout2Component,
loadChildren: () => import('./supplier/supplier.module').then(m => m.SupplierModule),
},
{
path: 'User',
component: Userapplayout2Component,
loadChildren: () => import('./user/user.module').then(m => m.UserModule),
},
{
path: 'MarketPlace',
//component: UsersApplayoutComponent,
//component: AppHeaderLayoutComponent,
component: Userapplayout2Component,
loadChildren: () => import('./marketplace/marketplace.module').then(m => m.MarketplaceModule),
},
{
path: 'User/Agrements',
//component: AppLayoutComponent,
component: AppHeaderLayoutComponent,
loadChildren: () => import('./agrements/agrements.module').then(m => m.AgrementsModule)
},
{
path: 'HWUser',
//component: AppLayoutComponent,
component: AppHeaderLayoutComponent,
loadChildren: () => import('./HelpAndFAQ/helpQuestion.module').then(m => m.HelpQuestionModule)
},
{
path: 'Tradesman',
component: Userapplayout2Component,
loadChildren: () => import('./trademan/trademan.module').then(m => m.TrademanModule),
canActivate: [AuthGuardTradesmanService],
canActivateChild: [AuthGuardTradesmanService]
},
{
path: 'ContactUs',
//component: AppLayoutComponent,
component: AppHeaderLayoutComponent,
loadChildren: () => import('./contactUs/contactUs.module').then(m => m.ContactUsModule)
},
{
path: 'landing-page/liveleads',
//component: AppLayoutComponent,
component: AppHeaderLayoutComponent,
loadChildren: () => import('./landing-page/liveleads/liveleads.module').then(m => m.LiveleadsModule)
},
{
path: 'landing-page',
component: AppHeaderLayoutComponent,
loadChildren: () => import('./landing-page/landing-page.module').then(m => m.LandingPageModule)
}
]
@NgModule({
declarations: [
AppComponent,
AppLayoutComponent,
AppDasboardHeaderComponent,
AppDasboardFooterComponent,
AppLeftmenuComponent,
AppcommonfooterComponent,
SupplierLayoutComponent,
TrademanLayoutComponent,
TrademenuLeftComponent,
SupplierLeftmenuComponent,
],
imports: [
BrowserModule.withServerTransition({ appId: 'ng-cli-universal' }),
NgbModule,
RouterModule.forRoot(routes, { enableTracing: false }),
ModalModule.forRoot(),
HttpModule,
BrowserAnimationsModule,
NgxImageCompressService,
Events,
Title,
metaTagsService,
ShareService,
],
schemas: [
CUSTOM_ELEMENTS_SCHEMA
],
bootstrap: [AppComponent]
})
export class AppModule { }
已在IIS上部署Web.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<location path="." inheritInChildApplications="false">
<system.webServer>
<handlers>
<add name="iisnode" path="main.js" verb="*" modules="iisnode"/>
</handlers>
<aspNetCore processPath="dotnet" arguments=".HW.Web2.dll" stdoutLogEnabled="false" stdoutLogFile=".logsstdout" />
<rewrite>
<rules>
<rule name="LogFile" patternSyntax="ECMAScript" stopProcessing="true">
<match url="iisnode"/>
</rule>
<rule name="StaticContent">
<action type="Rewrite" url="{{REQUEST_URI}}"/>
</rule>
<rule name="DynamicContent">
<conditions>
<add input="{{REQUEST_FILENAME}}" matchType="IsFile" negate="True"/>
</conditions>
<action type="Rewrite" url="main.js"/>
</rule>
</rules>
</rewrite>
<iisnode devErrorsEnabled="false"
debuggingEnabled="false"
loggingEnabled="false"
nodeProcessCommandLine="C:Program Filesnodejsnode.exe" />
</system.webServer>
</location>
</configuration>
您的angular应用程序的body标记无法在服务器端呈现有几个原因。这是一个清单:
- 首先确保您的生活环境支持NodeJS。如果服务器上没有NodeJS,就无法使用服务器端渲染
- 在Visual Studio中,尝试将
ASPNETCORE_ENVIRONMENT
环境变量从DEVELOPMENT
更改为PRODUCTION
,然后运行应用程序。在某些情况下,应用程序在两种配置中的行为都不同(当PRODUCTION
时,在另一个位置查找main.js
文件(。启动调试器并尝试预呈现视图后,您可能会在Visual Studio输出窗口中看到一些异常。- 在我的情况下,
main.js
文件必须以ClientApp/dist/main.js
结尾。所以我不得不修改angular.json
,将projects:ClientApp:architect:build:options:outputPath
更改为dist
(另请参阅(
- 在我的情况下,
- 如果您在使用Visual Studio时遇到此问题,请始终查看输出窗口以查找将为您指明正确方向的错误
- 如果您正在托管PWA(例如通过
@angular/pwa
(,那么当您在浏览器中访问查看源时,得到一个空页面是完全正常的。如果您选择ctrl+F5,您将绕过向您显示预先呈现的html源的角度服务工作者。这是你不应该为之烦恼的事情。谷歌,必应。。。将实际获取并索引页面的服务器端呈现版本 - 如果您使用的是ASP.NET Core 3.1或更早版本,则
SupplyData
委托不能有async
lambda。SupplyData
不是Func<Task>
,并且不会在ASP.NET Core的源代码中等待。我在端口中将其更改为.NET 6
options.SupplyData = async (context, data) => { ... };
- 一些实时环境会阻止您的angular应用程序在SSR期间发送回服务器的Web请求。在这种情况下,您将得到一个内部服务器错误(500(。您需要使用
OnSupplyData
来解决此问题