Angular Universal 不会接管客户端上的渲染

2024-01-29

我目前正在尝试按照官方角度将服务器端渲染(SSR)添加到我的角度应用程序中角度通用指南 https://angular.io/guide/universal。我能够设置快速服务器,并且从服务器获得的响应包含我的应用程序,尽管看起来有点奇怪,好像缺少一些 CSS 导致真棒字体 https://www.npmjs.com/package/@fortawesome/angular-fontawesome图标渲染太大等。

但出于某种原因,客户端/浏览器永远不会接管(重新)渲染,并且网站保持与服务器响应相同。它不是交互式的并且完全静态。单击控件(链接除外)或交互元素时,没有任何反应。

在这里你可以看到部分应用程序外壳 / HTML https://i.stack.imgur.com/hVpK0.png.

这里是网络流量 https://i.stack.imgur.com/oahHN.png.

The 包.json:

    {
      "name": "my-app",
      "version": "0.0.0",
      "scripts": {
        "ng": "ng",
        "start": "ng serve",
        "build": "ng build",
        "test": "ng test",
        "lint": "ng lint",
        "e2e": "ng e2e",
        "dev:ssr": "ng run my-app:serve-ssr",
        "serve:ssr": "node dist/my-app/server/main.js",
        "build:ssr": "ng build --prod && ng run my-app:server:production",
        "prerender": "ng run my-app:prerender"
      },
      "private": true,
      "dependencies": {
        "@agm/core": "^1.1.0",
        "@angular/animations": "^10.0.14",
        "@angular/common": "^10.0.14",
        "@angular/compiler": "^10.0.14",
        "@angular/core": "^10.0.14",
        "@angular/elements": "^10.0.14",
        "@angular/forms": "^10.0.14",
        "@angular/localize": "~10.0.14",
        "@angular/platform-browser": "^10.0.14",
        "@angular/platform-browser-dynamic": "^10.0.14",
        "@angular/platform-server": "^10.0.14",
        "@angular/router": "^10.0.14",
        "@angular/service-worker": "^10.0.14",
        "@fortawesome/angular-fontawesome": "^0.7.0",
        "@fortawesome/fontawesome-svg-core": "^1.2.30",
        "@fortawesome/free-brands-svg-icons": "^5.14.0",
        "@fortawesome/free-regular-svg-icons": "^5.14.0",
        "@fortawesome/free-solid-svg-icons": "^5.14.0",
        "@ng-bootstrap/ng-bootstrap": "^7.0.0",
        "@ng-select/ng-select": "^4.0.4",
        "@nguniversal/express-engine": "^10.0.2",
        "@webcomponents/webcomponentsjs": "^2.2.10",
        "bootstrap": "^4.3.1",
        "bufferutil": "^4.0.1",
        "core-js": "^2.5.4",
        "document-register-element": "^1.13.2",
        "domino": "^2.1.6",
        "express": "^4.15.2",
        "html-webpack-plugin": "^2.30.1",
        "intersection-observer": "^0.7.0",
        "jquery": "^3.5.1",
        "ng-lazyload-image": "^5.1.2",
        "ng5-slider": "^1.1.14",
        "ngx-cookie-service": "^2.2.0",
        "ngx-infinite-scroll": "^7.1.0",
        "popper.js": "^1.14.7",
        "rxjs": "^6.6.2",
        "tslib": "^2.0.0",
        "utf-8-validate": "^5.0.2",
        "webpack": "^4.44.1",
        "xmlhttprequest": "^1.8.0",
        "zone.js": "~0.10.2"
      },
      "devDependencies": {
        "@angular-devkit/build-angular": "~0.1000.8",
        "@angular/cli": "^10.0.8",
        "@angular/compiler-cli": "^10.0.14",
        "@angular/language-service": "^10.0.14",
        "@nguniversal/builders": "^10.0.2",
        "@types/express": "^4.17.0",
        "@types/jasmine": "~2.8.8",
        "@types/jasminewd2": "~2.0.3",
        "@types/node": "^12.11.1",
        "codelyzer": "^6.0.0",
        "jasmine-core": "~3.5.0",
        "jasmine-spec-reporter": "~5.0.0",
        "karma": "~5.0.0",
        "karma-chrome-launcher": "~3.1.0",
        "karma-coverage-istanbul-reporter": "~3.0.2",
        "karma-jasmine": "~3.3.0",
        "karma-jasmine-html-reporter": "^1.5.0",
        "ngx-perfect-scrollbar": "^7.2.1",
        "node-sass": "^4.14.1",
        "protractor": "~7.0.0",
        "ts-node": "~7.0.0",
        "tslint": "~6.1.0",
        "typescript": "^3.9.7"
      }
    }

The 角度.json:

{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
    "my-app": {
      "i18n": {
        "locales": {
          "de-AT": {
            "translation": "src/locale/messages.de-AT.xlf",
            "baseHref": ""
          }
        }
      },
      "root": "",
      "sourceRoot": "src",
      "projectType": "application",
      "prefix": "app",
      "schematics": {
        "@schematics/angular:component": {
          "style": "scss"
        }
      },
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "watch": false,
            "index": "src/index.html",
            "outputPath": "dist/my-app/browser",
            "main": "src/main.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "src/tsconfig.app.json",
            "assets": [
              "src/favicon.ico",
              "src/apple-touch-icon.png",
              "src/apple-touch-icon-precomposed.png",
              "src/robots.txt",
              "src/sitemap.xml",
              "src/assets",
              "src/manifest.json"
            ],
            "styles": [
              "src/styles.scss"
            ],
            "scripts": [
              {
                "input": "node_modules/document-register-element/build/document-register-element.js"
              }
            ]
          },
          "configurations": {
            "production": {
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ],
              "i18nMissingTranslation": "error",
              "localize": [
                "de-AT"
              ],
              "optimization": true,
              "outputHashing": "bundles",
              "sourceMap": false,
              "extractCss": true,
              "namedChunks": false,
              "aot": true,
              "extractLicenses": true,
              "vendorChunk": false,
              "buildOptimizer": true,
              "budgets": [
                {
                  "type": "initial",
                  "maximumWarning": "2mb",
                  "maximumError": "3mb"
                },
                {
                  "type": "anyComponentStyle",
                  "maximumWarning": "20kb"
                }
              ],
              "serviceWorker": true,
              "ngswConfigPath": "src/ngsw-config.json"
            },
            "dev": {
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.dev.ts"
                }
              ],
              "aot": false,
              "i18nMissingTranslation": "error",
              "localize": [
                "de-AT"
              ],
              "optimization": false,
              "outputHashing": "none",
              "extractCss": true,
              "namedChunks": true,
              "extractLicenses": true,
              "vendorChunk": false,
              "budgets": [
                {
                  "type": "initial",
                  "maximumWarning": "10mb",
                  "maximumError": "12mb"
                },
                {
                  "type": "anyComponentStyle",
                  "maximumWarning": "20kb"
                }
              ],
            }
          }
        },
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "options": {
            "browserTarget": "my-app:build"
          },
          "configurations": {
            "production": {
              "browserTarget": "my-app:build:production"
            }
          }
        },
        "extract-i18n": {
          "builder": "@angular-devkit/build-angular:extract-i18n",
          "options": {
            "browserTarget": "my-app:build"
          }
        },
        "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"
            ],
            "scripts": [],
            "assets": [
              "src/favicon.ico",
              "src/robots.txt",
              "src/sitemap.xml",
              "src/assets",
              "src/manifest.json"
            ]
          }
        },
        "lint": {
          "builder": "@angular-devkit/build-angular:tslint",
          "options": {
            "tsConfig": [
              "src/tsconfig.app.json",
              "src/tsconfig.spec.json",
              "src/tsconfig.server.json"
            ],
            "exclude": [
              "**/node_modules/**"
            ]
          }
        },
        "server": {
          "builder": "@angular-devkit/build-angular:server",
          "options": {
            "outputPath": "dist/my-app/server",
            "main": "server.ts",
            "tsConfig": "src/tsconfig.server.json"
          },
          "configurations": {
            "production": {
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ],
              "sourceMap": false,
              "optimization": true
            }
          }
        },
        "serve-ssr": {
          "builder": "@nguniversal/builders:ssr-dev-server",
          "options": {
            "browserTarget": "my-app:build",
            "serverTarget": "my-app:server"
          },
          "configurations": {
            "production": {
              "browserTarget": "my-app:build:production",
              "serverTarget": "my-app:server:production"
            }
          }
        },
        "prerender": {
          "builder": "@nguniversal/builders:prerender",
          "options": {
            "browserTarget": "my-app:build:production",
            "serverTarget": "my-app:server:production",
            "routes": [
              "/"
            ]
          },
          "configurations": {
            "production": {}
          }
        }
      }
    },
    "my-app-e2e": {
      "root": "e2e/",
      "projectType": "application",
      "prefix": "",
      "architect": {
        "e2e": {
          "builder": "@angular-devkit/build-angular:protractor",
          "options": {
            "protractorConfig": "e2e/protractor.conf.js",
            "devServerTarget": "my-app:serve"
          },
          "configurations": {
            "production": {
              "devServerTarget": "my-app:serve:production"
            }
          }
        },
        "lint": {
          "builder": "@angular-devkit/build-angular:tslint",
          "options": {
            "tsConfig": "e2e/tsconfig.e2e.json",
            "exclude": [
              "**/node_modules/**"
            ]
          }
        }
      }
    }
  },
  "defaultProject": "my-app"
}

The 服务器.ts:

import 'zone.js/dist/zone-node';

import { ngExpressEngine } from '@nguniversal/express-engine';
import * as express from 'express';
import { join } from 'path';

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

const template = fs
  .readFileSync(path.join('dist/my-app/browser/de-AT', 'index.html'))
  .toString();
const window = domino.createWindow(template);

// Ignite UI browser objects abstractions
(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;
(global as any).FocusEvent = window.FocusEvent;
(global as any).PointerEvent = window.PointerEvent;
(global as any).HTMLElement = window.HTMLElement;
(global as any).HTMLElement.prototype.getBoundingClientRect = () => {
    return {
      left: '',
      right: '',
      top: '',
      bottom: ''
  };
};

(global as any).XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest;

// Other optional depending on your application configuration
(global as any).object = window.object;
(global as any).navigator = window.navigator;
(global as any).localStorage = window.localStorage;
(global as any).DOMTokenList = window.DOMTokenList;

Object.defineProperty(window.document.body.style, 'transform', {
  value: () => {
    return {
      enumerable: true,
      configurable: true,
    };
  },
});
(global as any).CSS = null;
(global as any).Prism = null;

import { AppServerModule } from './src/main.server';
import { APP_BASE_HREF } from '@angular/common';
import { existsSync } from 'fs';

// 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/my-app/browser/de-AT');
  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}`);
  });
}

// 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';

The tsconfig.base.json:

{
  "compileOnSave": false,
  "compilerOptions": {
    "baseUrl": "./",
    "outDir": "./dist/out-tsc",
    "sourceMap": true,
    "declaration": false,
    "module": "es2020",
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "importHelpers": true,
    "target": "es2015",
    "downlevelIteration": true,
    "typeRoots": [
      "node_modules/@types",
      "typings.d.ts"
    ],
    "lib": [
      "es2018",
      "dom"
    ]
  }
}

The tsconfig.server.json:

{
  "extends": "./tsconfig.app.json",
  "compilerOptions": {
    "outDir": "../out-tsc/server",
    "target": "es2016",
    "types": [
      "node"
    ]
  },
  "files": [
    "main.server.ts",
    "../server.ts"
  ],
  "angularCompilerOptions": {
    "entryModule": "./app/app.server.module#AppServerModule"
  }
}

The 应用程序.server.module.ts:

    import { NgModule } from '@angular/core';
    import { ServerModule } from '@angular/platform-server';
    import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
    import { UniversalInterceptor } from './universal-interceptor';
    import { AppModule } from './app.module';
    import { AppComponent } from './app.component';

    @NgModule({
      imports: [
        AppModule,
        ServerModule
      ],
      providers: [
        {
          provide: HTTP_INTERCEPTORS,
          useClass: UniversalInterceptor,
          multi: true
        }
      ],
      bootstrap: [AppComponent]
    })
    export class AppServerModule {}

我真的不知道我做错了什么。有任何想法吗?


经过长时间的调查,我找到了罪魁祸首。似乎在添加 Angular Universal 时,CLI 包装了DOMContentLoaded事件侦听器围绕另一个应用程序引导应用程序DOMContentLoaded事件监听器在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', () => { // Remove this
  document.addEventListener('DOMContentLoaded', () => {
    platformBrowserDynamic().bootstrapModule(AppModule)
      .catch(err => console.error(err));
  });
}); // Remove this

因此,删除第二个事件侦听器会使应用程序再次引导。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Angular Universal 不会接管客户端上的渲染 的相关文章

随机推荐

  • SVG 和 Javascript - 创建 SVGPoint - TypeError:非法构造函数

    我正在尝试使用 Javascript 创建 SVG 多边形 当我尝试使用以下 Javascript 代码创建 SVGPoint 时 var p new SVGPoint 我收到以下消息 类型错误 非法构造函数 您需要从您的 SVG 文档中调
  • Espresso:如何滚动到 ScrollView 的底部

    在 Espresso 测试中如何向下滚动到 ScrollView 的底部 谢谢 如果在 ScrollView 的底部您需要找到一个视图并与之匹配某些内容 那么只需执行scrollTo 在需要显示它的任何其他操作之前对其执行操作 onView
  • C# 中的 Windows 时区下拉菜单 [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 是否有一种简单的方法可以在 C 应用程序中显示 Windows 时区下拉列表 以便用户可以更改它 类似于这个时区下拉菜单 https
  • 不要从 UITableView 中删除某些行

    我正在尝试实现从 a 中删除一些行的功能table view而不是其他人 在这种情况下 一切都在section 0不应该被删除 所以也不能滑动删除 但是里面的所有内容section 1应该可以 我怎样才能实现这个 现在section 0ro
  • Ui 表单:添加运行时还是编译时?

    如果我有大量 UI 表单 我想知道哪种方法有效 我对这两种情况下的内存利用率没有任何想法 我只是在一个简单的示例中尝试了两种方法 http qt project org doc qt 4 8 designer using a ui file
  • 检查 C 枚举中是否定义了某个值?

    假设我有这个 enum A 0x2E B 0x23 C 0x40 可以检查是否x被定义为enum 我正在手动执行此操作 int isdef x A x B x C 但我想要更有活力的东西 GCC extensions也欢迎 据我所知并非如此
  • 你可以在CSS文档中使用handlebars.js变量吗?

    我有兴趣做一些条件格式并认为最好的方法是做类似的事情 InsideCSSdocument if something some css else some other css if 但我搜索了一下 只发现Django相关的东西 这在车把或任何
  • 下载S3并添加到应用程序目录

    我有一个正在尝试设置的弹性 beanstalk Node 应用程序 我正在尝试将环境文件从 S3 加载到 var app ondeck src 因此该文件在 Node 应用程序启动时可用 我已经尝试了尽可能多的方法 但 production
  • 什么是双变量参数? ~ 打字稿

    TypeScript 的文档附带编译器选项 https www typescriptlang org docs handbook compiler options html 其中 strictFunctionTypes是用它的描述来定义的
  • Python tkFileDialog.asksaveasfile - 获取文件路径

    我想获取文件 exportFile 的路径 exportFile tkFileDialog asksaveasfile mode a 如果我写 打印导出文件 我会得到
  • 想要延迟 1 秒显示数字

    我想在 TextView 中显示数字 如下所示 Wait 5 sec then a delay of 1 sec Wait 4 sec display this in the same text view along with delay
  • 哪里可以获取以前版本的 iOS 磁盘映像? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我刚刚成为 iOS 开发者计划的成员 我正在阅读 iOS 开发者计划用户指南文档 它说 从 iOS 开
  • GPL 对 javascript 库的限制 [关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 如果我在我的网站上使用 GPL 许可的 JavaScript 组件 是否会被视为向公众发布 因为组件的客户端代码通过 http 加载到用户的浏览器 并
  • Python:如何使用Python生成随机稀疏对称矩阵?

    如何使用python生成随机稀疏对称矩阵 在 MATLAB 中 我们有一个函数 sprandsym 大小 密度 http www mathworks fr help matlab ref sprandsym html 但如何在 Python
  • jQuery 上传插件可与 JSP/Java 配合使用

    我正在努力完成以下任务 通过将表单 enctype multipart form data 提交给 JSP 操作来将文件上传到应用程序 JSP 操作会处理其余部分 包括将文件写入磁盘 处理和返回有关上传的一些 xml 数据 直到最近 我还在
  • “import pandas.io.data as web ”给我一个错误,说没有 pandas.io.data 的模块名称

    我刚刚学习 python 并尝试用它进行股票分析 使用库存统计 我安装了stockstats pip 安装 stockstats 进口大熊猫 进口熊猫 尝试导入数据 导入 pandas io data 收到错误消息说模块 pandas io
  • 应用内购买响应。产品为空

    我知道以前有人问过 iPhone 应用程序内购买 响应 产品仍然是空的 https stackoverflow com questions 2138972 iphone in app purchase response products a
  • 发布到编辑控制器操作不传递模型 ID

    我的控制器上有一个如下所示的操作 HttpPost public ActionResult Edit EditMyObjectViewModel editMyObjectViewModel EditMyActionViewModel包含一个
  • SpringMVC: 必须在同一上下文中吗?

    ApplicationContext 是 WebApplicationContext 的父级 但为什么 和 必须放在相同的上下文中才能起作用 这是我的解释 我像这样配置我的 applicationConext xml
  • Angular Universal 不会接管客户端上的渲染

    我目前正在尝试按照官方角度将服务器端渲染 SSR 添加到我的角度应用程序中角度通用指南 https angular io guide universal 我能够设置快速服务器 并且从服务器获得的响应包含我的应用程序 尽管看起来有点奇怪 好像