Angular Universal 正在为每个页面在页面源中呈现主页 html

Posted

技术标签:

【中文标题】Angular Universal 正在为每个页面在页面源中呈现主页 html【英文标题】:Angular universal is rendering home page html in page source for every pages 【发布时间】:2018-08-23 22:07:28 【问题描述】:

我的 Angular 通用项目中有多个组件。当我从主组件路由到其他组件时,我只在页面源中获取主组件的内容。它没有被路由组件更新。

我已经通过服务器端的console.log检查了每个组件是否在服务器端呈现,但它只是加载主页,其他组件没有在服务器端呈现。

我使用 npm run build:s-s-r 进行构建,npm run serve:s-s-r 用于在服务器端提供服务。

在 cmd 或浏览器控制台中运行上述命令时也没有错误。

有人可以帮忙吗!我现在被这个问题困扰了大约一个星期。

如果你们需要有关该项目的任何其他信息,请告诉我。

Package.json

"dependencies": 
    "@agm/core": "^1.0.0-beta.2",
    "@angular/animations": "^5.2.0",
    "@angular/common": "^5.2.0",
    "@angular/compiler": "^5.2.0",
    "@angular/core": "^5.2.0",
    "@angular/forms": "^5.2.0",
    "@angular/http": "^5.2.0",
    "@angular/platform-browser": "^5.2.0",
    "@angular/platform-browser-dynamic": "^5.2.0",
    "@angular/platform-server": "^5.2.8",
    "@angular/router": "^5.2.0",
    "@ngu/carousel": "^1.4.8",
    "@nguniversal/express-engine": "^5.0.0-beta.6",
    "@nguniversal/module-map-ngfactory-loader": "^5.0.0-beta.6",
    "bootstrap": "^3.3.7",
    "core-js": "^2.4.1",
    "hammerjs": "^2.0.8",
    "ng2-nouislider": "^1.7.7",
    "ngx-bootstrap": "^2.0.0-beta.8",
    "nouislider": "^11.0.3",
    "reflect-metadata": "^0.1.12",
    "rxjs": "^5.5.6",
    "ts-loader": "^3.5.0",
    "zone.js": "^0.8.19"
  ,
  "devDependencies": 
    "@angular/cli": "~1.7.3",
    "@angular/compiler-cli": "^5.2.0",
    "@angular/language-service": "^5.2.0",
    "@types/jasmine": "~2.8.3",
    "@types/jasminewd2": "~2.0.2",
    "@types/node": "~6.0.60",
    "codelyzer": "^4.0.1",
    "cpy-cli": "^1.0.1",
    "jasmine-core": "~2.8.0",
    "jasmine-spec-reporter": "~4.2.1",
    "karma": "~2.0.0",
    "karma-chrome-launcher": "~2.2.0",
    "karma-coverage-istanbul-reporter": "^1.2.1",
    "karma-jasmine": "~1.1.0",
    "karma-jasmine-html-reporter": "^0.2.2",
    "localstorage-polyfill": "^1.0.1",
    "protractor": "~5.1.2",
    "ts-node": "~4.1.0",
    "tslint": "~5.9.1",
    "typescript": "~2.5.3"
  

Server.ts

// These are important and needed before anything else
import 'zone.js/dist/zone-node';
import 'reflect-metadata';

import  renderModuleFactory  from '@angular/platform-server';
import  enableProdMode  from '@angular/core';

import * as express from 'express';
import  join  from 'path';
import  readFileSync  from 'fs';
import 'localstorage-polyfill';

// Faster server renders w/ Prod mode (dev mode never needed)
enableProdMode();

// Express server
const app = express();

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

// Our index.html we'll use as our template
const template = readFileSync(join(DIST_FOLDER, 'browser', 'index.html')).toString();

// jay
const domino = require('domino');
const win = domino.createWindow(template);
global['window'] = win;
global['document'] = win.document;
global['localStorage'] = localStorage;
// jay

// * NOTE :: leave this as require() since this file is built Dynamically from webpack
const  AppServerModuleNgFactory, LAZY_MODULE_MAP  = require('./dist/server/main.bundle');

const  provideModuleMap  = require('@nguniversal/module-map-ngfactory-loader');

app.engine('html', (_, options, callback) => 
  renderModuleFactory(AppServerModuleNgFactory, 
    // Our index.html
    document: template,
    url: options.req.url,
    // DI so that we can get lazy-loading to work differently (since we need it to just instantly render it)
    extraProviders: [
      provideModuleMap(LAZY_MODULE_MAP)
    ]
  ).then(html => 
    callback(null, html);
  );
);

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

// Server static files from /browser
app.get('*.*', express.static(join(DIST_FOLDER, 'browser')));

// All regular routes use the Universal engine
app.get('*', (req, res) => 
  res.render(join(DIST_FOLDER, 'browser', 'index.html'),  req );
);

// Start up the Node server
app.listen(PORT, () => 
  console.log(`Node server listening on http://localhost:$PORT`);
);

app.server.module.ts

import NgModule from '@angular/core';
import ServerModule from '@angular/platform-server';
import ModuleMapLoaderModule from '@nguniversal/module-map-ngfactory-loader';

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

@NgModule(
  imports: [
    // The AppServerModule should import your AppModule followed
    // by the ServerModule from @angular/platform-server.
    AppModule,
    ServerModule,
    ModuleMapLoaderModule // <-- *Important* to have lazy-loaded routes work
  ],
  // Since the bootstrapped component is not inherited from your
  // imported AppModule, it needs to be repeated here.
  bootstrap: [AppComponent],
)
export class AppServerModule 

app.module.ts

// Other modules also imported here deleting that for saving space
import  routingComponents, AppRoutingModule  from './app.routing.module';
import  BrowserModule  from '@angular/platform-browser';
import  NgModule  from '@angular/core';
import  FormsModule, ReactiveFormsModule  from '@angular/forms';
import  AppComponent  from './app.component';
import  HttpModule  from '@angular/http';
import  HttpClientModule  from '@angular/common/http';

@NgModule(
  declarations: [
    AppComponent,

    // Imported other components here

  ],
  imports: [
    BrowserModule.withServerTransition( appId: 'universal-cli' ),
    FormsModule,
    ReactiveFormsModule,
    HttpModule,
    HttpClientModule,
    ModalModule,
    NguCarouselModule
  ],
  providers: [
    // added providers here
  ],
  entryComponents: [],
  bootstrap: [AppComponent]
)
export class AppModule  

.angular-cli.json


  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "project": 
    "name": "universal-cli"
  ,
  "apps": [
    
      "root": "src",
      "outDir": "dist/browser",
      "assets": [
        "assets",
        "favicon.ico"
      ],
      "index": "index.html",
      "main": "main.ts",
      "polyfills": "polyfills.ts",
      "test": "test.ts",
      "tsconfig": "tsconfig.app.json",
      "testTsconfig": "tsconfig.spec.json",
      "prefix": "app",
      "styles": [
       "styles.css",
      ],
      "scripts": [],
      "environmentSource": "environments/environment.ts",
      "environments": 
        "dev": "environments/environment.ts",
        "prod": "environments/environment.prod.ts"
      
    ,
    
      "name": "s-s-r",
      "platform": "server",
      "root": "src",
      "outDir": "dist/server",
      "assets": [
        "assets",
        "favicon.ico"
      ],
      "index": "index.html",
      "main": "main.server.ts",
      "test": "test.ts",
      "tsconfig": "tsconfig.server.json",
      "testTsconfig": "tsconfig.spec.json",
      "prefix": "app",
      "styles": [
        "styles.css"
      ],
      "scripts": [],
      "environmentSource": "environments/environment.ts",
      "environments": 
        "dev": "environments/environment.ts",
        "prod": "environments/environment.prod.ts"
      
    
  ],
  "e2e": 
    "protractor": 
      "config": "./protractor.conf.js"
    
  ,
  "lint": [
    
      "project": "src/tsconfig.app.json",
      "exclude": "**/node_modules/**"
    ,
    
      "project": "src/tsconfig.spec.json",
      "exclude": "**/node_modules/**"
    ,
    
      "project": "e2e/tsconfig.e2e.json",
      "exclude": "**/node_modules/**"
    
  ],
  "test": 
    "karma": 
      "config": "./karma.conf.js"
    
  ,
  "defaults": 
    "styleExt": "css",
    "component": 
  

【问题讨论】:

如果直接在地址栏中导航到另一个url,按ctrl+U时是否得到正确的源代码? 那是你直接使用4000端口的时候? 不,我只是想确保您的测试正确。我已经看到用户没有测试正确的东西的问题,或者期望页面的源在导航客户端而不是直接输入 url 时会发生变化。如果您转到http://yourserver:4000 并按Ctrl+U,您应该会在app-root 标签中看到一些内容(如果它正在工作)。如果你也去http://yourserver:4000/someroute,你应该在app-root看到不同的内容。 感谢@David 的详细解释。我已经测试过,和你提到的一样。在yourserver:4000 并按 Ctrl+U 我正在获取 app-root 中的内容,但是当我转到 yourserver:4000/someroute 并按 Ctrl+U 时,app-root 中的内容就是我为 yourserver:4000 得到的内容并按 Ctrl +U,它没有得到更新。我还更新了页面的标题,页面也没有更新。 好的。你的server.ts 对我来说似乎是正确的。而且您在控制台服务器端没有任何错误?您正在使用延迟加载路由测试其他路由吗? .也许在您的某些组件中添加一些控制台日志以查看它们是否被称为服务器端。如果使用 angular-cli,您还可以发布您的 app.model.server.ts、main.server.ts 和 .angular-cli.json 【参考方案1】:

问题在于 URL 结构。 我们使用 HashLocationStrategy https://codecraft.tv/courses/angular/routing/routing-strategies/ 在 URL 中添加 # 以避免在服务器中刷新 angular 页面时出现 404 页面错误。

当我们从应用程序中删除 HashLocationStrategy 后,它就可以正常工作了。

@david:感谢您的支持。

【讨论】:

花费了全部时间来解决这个问题。那就是答案 @Alwin Richard 我们也面临这个错误,但我们没有在项目中使用 HashLocationStrategy @rajpootrehan 你解决了吗?我没有使用 # 策略,我也在为同样的事情而苦苦挣扎。我已经设置了角度通用,我可以看到相同的行为。仅从服务器获取主页 html,并且不会随着更改路由而更新 此问题出现在与身份验证相关的页面上。如果您登录并刷新页面,那么它将首先显示主页内容,并且在验证身份验证后它将自动通过路由重定向。

以上是关于Angular Universal 正在为每个页面在页面源中呈现主页 html的主要内容,如果未能解决你的问题,请参考以下文章

查看页面源 Angular Universal 和 Angular 11

Angular 2 Universal 404 Not Found 重定向

Angular Universal 不在路由器插座内呈现内容

(Angular 6) Angular Universal - 不等待内容 API 调用

Angular Universal:添加服务工作者不允许页面的特定标题

Angular Universal 不适用于 Angular Google 地图