带有传单和ngx传单的Angular UniversalSSR

问题描述

我刚开始使用angular(更不用说noob)了,由于Leaflet的问题,我正在努力将标准的Angular应用程序传递给Angular Universal,我发现许多项目都可以正常工作,但是我无法管理使它在我的项目中以相同的方式工作。 我设法隔离了这个问题,但我无法解决。 我删除了所有组件和模块中对Leaflet的所有引用,只剩下了package.json,如下所示:

  "name": "vyv-angular","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 vyv-angular:serve-ssr","serve:ssr": "node dist/vyv-angular/server/main.js","build:ssr": "ng build --prod && ng run vyv-angular:server:production","prerender": "ng run vyv-angular:prerender"
  },"private": true,"dependencies": {
    "@agm/core": "3.0.0-beta.0","@angular/animations": "10.1.5","@angular/cdk": "10.2.4","@angular/common": "10.1.5","@angular/compiler": "10.1.5","@angular/core": "10.1.5","@angular/fire": "6.0.3","@angular/forms": "10.1.5","@angular/localize": "10.1.5","@angular/material": "10.2.4","@angular/platform-browser": "^10.1.5","@angular/platform-browser-dynamic": "10.1.5","@angular/platform-server": "^10.1.5","@angular/router": "10.1.5","@asymmetrik/ngx-leaflet": "8.1.0","@ng-bootstrap/ng-bootstrap": "7.0.0","@ng-bootstrap/schematics": "2.0.0-alpha.1","@ng-select/ng-select": "5.0.3","@ngrx/store": "10.0.0","@nguniversal/express-engine": "10.1.0","@nguniversal/module-map-ngfactory-loader": "8.2.6","@ngx-translate/core": "13.0.0","@ngx-translate/http-loader": "6.0.0","@swimlane/ngx-datatable": "18.0.0","acorn": "8.0.1","agm-direction": "0.8.7","body-parser": "1.19.0","bootstrap": "4.0.0","classlist.js": "1.1.20150312","core-js": "3.6.5","cors": "2.8.5","d3": "6.1.1","datatables.net": "1.10.22","date-fns": "2.16.1","express": "4.17.1","firebase": "7.23.0","google-libphonenumber": "^3.2.13","hopscotch": "0.3.1","intl": "1.2.5","intl-tel-input": "17.0.6","leaflet": "1.7.1","leaflet-ant-path": "1.3.0","material-design-icons": "3.0.1","md5-typescript": "1.0.5","moment": "2.28.0","ng-apexcharts": "1.5.1","ng-click-outside": "7.0.0","ngx-bootstrap": "^6.1.0","ngx-custom-validators": "9.1.0","ngx-device-detector": "2.0.0","ngx-intl-tel-input": "^3.0.3","ngx-perfect-scrollbar": "10.0.1","ngx-quill": "12.0.1","ngx-spinner": "10.0.1","ngx-take-until-destroy": "5.4.0","ngx-toastr": "13.0.0","ngx-ui-switch": "10.0.2","node-sass": "4.14.1","nodemailer": "6.4.13","nouiSlider": "14.6.2","popper.js": "1.16.1","postcss-rtl": "1.7.3","prismjs": "1.21.0","pug": "3.0.0","quill": "1.3.7","rxjs": "6.6.3","rxjs-compat": "^6.6.3","screenfull": "5.0.2","tslib": "2.0.1","web-animations-js": "2.3.2","zone.js": "0.11.1"
  },"devDependencies": {
    "@angular-devkit/architect": "0.1001.6","@angular-devkit/build-angular": "0.1001.6","@angular/cli": "10.1.6","@angular/compiler-cli": "10.1.5","@nguniversal/builders": "10.1.0","@types/chartist": "0.11.0","@types/cors": "2.8.8","@types/d3-shape": "1.3.2","@types/express": "4.17.0","@types/google-maps": "3.2.2","@types/googlemaps": "3.39.12","@types/jasmine": "3.5.0","@types/jasminewd2": "2.0.3","@types/jquery": "3.5.2","@types/node": "14.11.8","@types/nodemailer": "6.4.0","codelyzer": "6.0.1","firebase-admin": "9.2.0","firebase-functions": "3.6.0","firebase-functions-test": "0.2.2","firebase-tools": "8.12.1","fuzzy": "0.1.3","inquirer": "6.2.2","inquirer-autocomplete-prompt": "1.0.1","jasmine-core": "3.6.0","jasmine-spec-reporter": "4.2.1","karma": "5.0.0","karma-chrome-launcher": "3.1.0","karma-coverage-istanbul-reporter": "2.1.0","karma-jasmine": "3.0.1","karma-jasmine-html-reporter": "1.4.2","open": "7.3.0","protractor": "7.0.0","rxjs-compat": "6.6.3","tslint": "6.1.0","typescript": "4.0.3"
  }
}

然后

ng run dev:ssr

运行正常,该应用程序可用于服务器端渲染(当然,映射部分已被删除,但除外)。

然后,我开始逐步重新介绍ngx-leaflet模块。 第一步是简单地将模块添加到app.module.ts

import {LeafletModule} from '@asymmetrik/ngx-leaflet';

以及进口环节:

 imports: [
LeafletModule,

只需添加内容,而无需任何组件使用(我将我的地图组件留空) 给我以下错误

    > ng run vyv-angular:serve-ssr

****************************************************************************************
This is a simple server for use in testing or debugging Angular applications locally.
It hasn't been reviewed for security issues.

DON'T USE IT FOR PRODUCTION!
****************************************************************************************
Hash: c84349b0ffb562a756f2
Time: 40944ms
Built at: 2020-10-11 9:17:28 ├F10: AM┤
      Asset      Size  Chunks                          Chunk Names
    main.js  13.3 MiB    main  [emitted]        [big]  main
main.js.map  13.2 MiB    main  [emitted] [dev]         main
Entrypoint main [big] = main.js main.js.map
chunk {main} main.js,main.js.map (main) 11.4 MiB [entry] [rendered]

chunk {main} main.js,main.js.map (main) 1.36 MB [initial] [rendered]
chunk {polyfills} polyfills.js,polyfills.js.map (polyfills) 147 kB [initial] [rendered]
chunk {runtime} runtime.js,runtime.js.map (runtime) 6.15 kB [entry] [rendered]
chunk {styles} styles.css,styles.css.map (styles) 1.36 MB [initial] [rendered]
chunk {vendor} vendor.js,vendor.js.map (vendor) 8.77 MB [initial] [rendered]
Date: 2020-10-11T14:17:45.082Z - Hash: bde98637bf23c20bf894 - Time: 50331ms

Compiled successfully.
F:\GitaLab\vyv-angular\dist\vyv-angular\server\main.js:65133
  var requestFn = window.requestAnimationFrame || getPrefixed('RequestAnimationFrame') || timeoutDefer;
                  ^

ReferenceError: window is not defined
    at F:\GitaLab\vyv-angular\dist\vyv-angular\server\main.js:65133:19
    at F:\GitaLab\vyv-angular\dist\vyv-angular\server\main.js:64914:11
    at Object.4R65 (F:\GitaLab\vyv-angular\dist\vyv-angular\server\main.js:64916:2)
    at __webpack_require__ (F:\GitaLab\vyv-angular\dist\vyv-angular\server\main.js:20:30)
    at F:\GitaLab\vyv-angular\dist\vyv-angular\server\main.js:116999:129
    at Object.RlJC (F:\GitaLab\vyv-angular\dist\vyv-angular\server\main.js:117001:2)
    at __webpack_require__ (F:\GitaLab\vyv-angular\dist\vyv-angular\server\main.js:20:30)
    at Module.ZAI4 (F:\GitaLab\vyv-angular\dist\vyv-angular\server\main.js:134326:82)
    at __webpack_require__ (F:\GitaLab\vyv-angular\dist\vyv-angular\server\main.js:20:30)
    at Module.24aS (F:\GitaLab\vyv-angular\dist\vyv-angular\server\main.js:54859:69)

A server error has occurred.
node exited with 1 code.
connect ECONNREFUSED 127.0.0.1:58155

我确定“ var requestFn = window.requestAnimationFrame || getPrefixed('RequestAnimationFrame')|| timeoutDefer;”这行是在leaflet-src.js内部,在node-modules /下,所以它是失败的窗口的javascript引用,这是正常的,因为在服务器上运行,但是如果我什至不触发,为什么会出现此错误脚本的任何用法? 我设法克隆了工作示例的存储库,但package.json,app.module.ts和其他组件与我的相同。因此,问题出在其他地方,但我找不到地方。 我是否只是以错误的方式将我的标准Angular应用程序迁移到Angular Universal? 我遵循了此运行的标准准则:

ng add @nguniversal/express-engine

一切都很好。

然后我也跑了:

ng add @angular/fire

为了能够作为功能在Firebase中进行部署,但目前问题不是部署部分,所以SSR服务器不是从app.module.ts中导入的ngx-leaflet模块开始的。 / p>

我应该添加其他文件来帮助吗? ng add @ nguniversal / express-engine为服务器和ts配置创建了新文件,但我没有对其进行修改...

欢迎任何使我步入正轨的想法或评论

编辑:

我克隆的工作示例和我的工作示例之间的唯一区别是,ngx模块的导入是在仅通过路由器加载的子模块中完成的。 我尝试在那些项目中将导入添加到主app.modules.ts中,并且发生相同的错误! 因此,此问题似乎仅在此模块的导入位于顶层时才发生。 我将尝试在一个单独的模块中实现我的地图组件,并在找到合适的解决方案时使该帖子保持最新状态。

解决方法

该错误消息很清楚,这是由于您使用了窗口对象。您可以识别浏览器并仅在浏览器内部执行代码,如下所示:

import { isPlatformBrowser } from '@angular/common';
import { Component,Inject,OnInit,PLATFORM_ID } from '@angular/core';

@Component({
  selector: 'app-yourcomponent',templateUrl: './app.yourcomponent.html',})
export class YourComponent implements OnInit {
  constructor(
    @Inject(PLATFORM_ID) private _platformId
  ) {}

  ngOnInit() {
    if (isPlatformBrowser(this._platformId)) {
      // Whatever you want to run inside the browser
      window.scrollTo(0,0);
    }
  }
}
,

好几天后,我决定删除ngx-leaflet模块。 根据本期的How to use @angular/universal with Leaflet?,我基于Sergey的想法使传单地图与原始javascript库一起使用。 这意味着对该组件进行了一些修改,因为失去了由ngx-leaflet提供的角度内的小叶整合的简便性,但这值得一试。

解决方案正如Sergey所解释的那样,是将对传单脚本的访问权包装在服务中,但是在我的项目中,服务中使用的“ require” javascript方法无法识别,即使“ @ types /节点”已正确安装。解决下一个问题的方法是修改tsconfig.app.json并在编译器选项中添加以下内容:“ types”:[“ node”],在我的情况下,结果如下:

  "compilerOptions": {
    "outDir": "./out-tsc/app","types": [ "node" ],"typeRoots": [ "../node_modules/@types" ]
  },

因为它已经存在于tsconfig.server.json中,但这还不够。

然后,在您的参数中(也如Sergei所述),请小心不要使用或导入任何Leflet方法,不过“ @ types / leaflet”是可以的。 对“ L”传单脚本的所有引用都必须通过服务完成,该服务必须嵌入测试浏览器存在的测试中:
  if (isPlatformBrowser(this.platformId)) {
     this.leafletService.L.
  }