【问题标题】:Angular 9 Universal ReferenceError: window is not definedAngular 9 Universal ReferenceError:未定义窗口
【发布时间】:2020-05-28 00:52:51
【问题描述】:

我正在尝试使用一些第三方库在 Angular 9 项目上设置 SSR,但没有成功。

下面的错误

ReferenceError: window is not defined
at ./node_modules/intl-tel-input/build/js/intlTelInput.js (main.js:295050:32)
at Object../node_modules/intl-tel-input/build/js/intlTelInput.js (main.js:295052:3)
at __webpack_require__ (main.js:20:30)
at Object../node_modules/intl-tel-input/index.js (main.js:296381:18)
at __webpack_require__ (main.js:20:30)
at Module../node_modules/intl-tel-input-ng/__ivy_ngcc__/fesm2015/intl-tel-input-ng.js (main.js:294832:72)
at __webpack_require__ (main.js:20:30)
    at Object.. main.js:463107:29)
    at __webpack_require__ (main.js:20:30)
    at Object.. main.js:462977:33)
A server error has occurred.
node exited with 1 code.
connect ECONNREFUSED 127.0.0.1:50362

我无权访问服务器端的窗口对象,这很正常,因此为避免此错误,我正在检查平台并在需要 intl-tel-input lib 的组件中使用条件逻辑...无需成功。

如何修复 SSR 时的窗口、导航器、文档错误?

package.json 文件下方

    {
  "name": "dz",
  "version": "0.0.0",
  "scripts": {

    "compile:server_bak": "webpack --mode production --config webpack.server.config.js --progress --colors",
    "build:ssr_bak": "npm run build:client-and-server-bundles && npm run compile:server",
    "serve:ssr_bak": "node dist/server",
    "build:client-and-server-bundles_bak": "npm run build-prod && ng run dz:server:production",
    "dev:ssr": "ng run dz:serve-ssr",
    "serve:ssr": "node dist/server/main.js",
    "build:ssr": "ng build --prod && ng run dz:server:production",
    "prerender": "ng run dz:prerender"
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "^9.0.0",
    "@angular/cdk": "^9.0.0",
    "@angular/common": "^9.0.0",
    "@angular/compiler": "^9.0.0",
    "@angular/core": "^9.0.0",
    "@angular/forms": "^9.0.0",
    "@angular/google-maps": "^9.0.0-rc.0",
    "@angular/localize": "^9.0.0-next.5",
    "@angular/material": "^9.0.0",
    "@angular/platform-browser": "^9.0.0",
    "@angular/platform-browser-dynamic": "^9.0.0",
    "@angular/platform-server": "^9.0.0",
    "@angular/pwa": "^0.803.23",
    "@angular/router": "^9.0.0",
    "@angular/service-worker": "^9.0.0",
    "@kolkov/angular-editor": "^1.0.3",
    "@ng-bootstrap/ng-bootstrap": "^5.2.1",
    "@ng-toolkit/universal": "^8.0.3",
    "@nguniversal/common": "~9.0.0",
    "@nguniversal/express-engine": "^9.0.0",
    "@ngx-translate/core": "^11.0.1",
    "@ngx-translate/http-loader": "^4.0.0",
    "@nicky-lenaers/ngx-scroll-to": "^3.0.1",
    "@sentry/browser": "^5.11.1",
    "@smip/ngx-materialize": "^0.3.0",
    "@swimlane/ngx-charts": "^13.0.2",
    "@trilon/ng-universal": "^2.1.0",
    "@types/express": "^4.17.1",
    "@types/lodash": "^4.14.141",
    "acorn": "^7.1.0",
    "angular-calendar": "0.27.9",
    "angular-in-memory-web-api": "~0.9.0",
    "angular-resizable-element": "^3.3.0",
    "angular2-image-upload": "1.0.0-rc.2",
    "body-parser": "^1.19.0",
    "bootstrap": "^4.4.1",
    "calendar-utils": "0.7.0",
    "cookie-parser": "^1.4.4",
    "cookieconsent": "^3.1.1",
    "core-js": "^3.6.4",
    "cors": "~2.8.5",
    "date-fns": "^1.30.1",
    "domino": "^2.1.4",
    "express": "^4.17.1",
    "express-http-proxy": "^1.6.0",
    "font-awesome": "^4.7.0",
    "google-libphonenumber": "^3.2.6",
    "gzip-all": "^1.0.0",
    "imagesloaded": "^4.1.4",
    "intersection-observer": "^0.7.0",
    "intl-tel-input": "16.0.10",
    "intl-tel-input-ng": "0.1.0",
    "materialize-css": "^1.0.0",
    "mock-browser": "^0.92.14",
    "moment": "^2.24.0",
    "ng2-sticky-kit": "^6.1.0",
    "ngx-cookieconsent": "^2.2.3",
    "ngx-google-places-autocomplete": "^2.0.4",
    "ngx-lightbox": "^2.1.0",
    "ngx-moment": "^3.5.0",
    "ngx-pagination": "5.0.0",
    "ngx-progressbar": "^6.0.1",
    "ngx-quicklink": "^0.2.0",
    "ngx-scrollspy": "^1.2.1",
    "path": "^0.12.7",
    "popper.js": "^1.16.1",
    "positioning": "1.4.0",
    "preboot": "^7.0.0",
    "reflect-metadata": "^0.1.13",
    "rxjs": "^6.5.4",
    "rxjs-compat": "^6.5.4",
    "sweetalert2": "^9.7.0",
    "ts-helpers": "^1.1.2",
    "tslib": "^1.10.0",
    "typings": "^2.1.1",
    "webpack": "^4.41.5",
    "workbox-sw": "^4.3.1",
    "zone.js": "~0.10.2"
  },
  "devDependencies": {
    "@angular-devkit/architect": "^0.900.1",
    "@angular-devkit/build-angular": "^0.900.1",
    "@angular/cli": "^9.0.1",
    "@angular/compiler-cli": "^9.0.0",
    "@angular/language-service": "^9.0.0",
    "@types/jasminewd2": "~2.0.6",
    "@types/jquery": "^3.2.12",
    "firebase-tools": "^6.10.0",
    "fuzzy": "^0.1.3",
    "http-server": "^0.11.1",
    "inquirer": "^6.2.2",
    "inquirer-autocomplete-prompt": "^1.0.1",
    "jasmine-core": "~3.5.0",
    "jasmine-spec-reporter": "~4.2.1",
    "karma-jasmine": "~2.0.1",
    "karma-jasmine-html-reporter": "^1.5.1",
    "protractor": "~6.0.0",
    "ts-loader": "^5.4.5",
    "@nguniversal/builders": "^9.0.0",
    "@types/express": "^4.17.0",
    "@types/node": "^12.11.1",
    "@types/jasmine": "~3.5.0",
    "codelyzer": "^5.1.2",
    "karma": "~4.3.0",
    "karma-chrome-launcher": "~3.1.0",
    "karma-coverage-istanbul-reporter": "~2.1.0",
    "ts-node": "~8.3.0",
    "tslint": "~5.17.0",
    "typescript": "~3.7.5",
    "webpack-cli": "^3.3.10",
    "webpack-node-externals": "^1.7.2"
  },
  "budgets": [
    {
      "type": "initial",
      "maximumWarning": "2mb",
      "maximumError": "5mb"
    }
  ]
}

【问题讨论】:

  • 你解决过这个问题吗?我也有这个问题。即使使用 SSR,在 Angular 8 中也不是问题。

标签: node.js angular server-side-rendering


【解决方案1】:

尝试以这种方式使用窗口对象

在.ts文件中导入

import { WINDOW } from '@ng-toolkit/universal';

然后在构造函数中注入它

constructor(@Inject(WINDOW) public window: Window) {

}

并在下面的任何函数中使用它

getBlogs() {
    this.blogsService.getBlogs().subscribe((res) => {
      if (res.message === 'success') {
        this.blogs = res.data.blogs;
        this.window.scroll(0, 0);
      } else {
        console.log('No blogs');
      }
    }, (err) => {
      console.log(err);
    });
  }

【讨论】:

  • 我不想直接使用Window Object,它需要在第三方lib(intl-tel-input-ng)中。
  • 我知道它已经很晚了,但对其他人来说。对于第三方包中的窗口,您可以在 server.ts 中添加我上面给出的一段代码。
【解决方案2】:

如果您有多个组件需要使用窗口。还有另一种修复此错误的方法,您无需导入或安装任何第三方包。

此错误的原因是,在节点服务器执行或解析代码时,未定义窗口。因此,为避免出现这种情况,我们可以设置像 if (isPlatformBrowser(this.platformId)) 这样的 if 条件,仅当脚本在浏览器级别运行时才会执行

import { isPlatformBrowser } from '@angular/common';


@Component({
  selector: 'space-app',
  templateUrl: './app.component.html'
})

export class AppComponent {

  constructor(
    @Inject(PLATFORM_ID) private platformId: any
  ) { }

  ngOnInit() {}

  onActivate() {
    if (isPlatformBrowser(this.platformId)) {
      window.scrollTo(0, 0);
    }
  }
}

这适用于您将直接使用window 的地方。但是,如果任何第三方 Angular 包正在使用您不知道的 window 怎么办。为了防止这些情况下的错误。我们需要在server.ts中添加一段代码

const path = require('path');
const domino = require('domino');
const templateA = fs.readFileSync(path.join('dist/browser', 'index.html')).toString();
const win = domino.createWindow(templateA);

global['window'] = win;
global['document'] = win.document;
// Express server
export const app = express();

但要确保export const app = express()的位置正确

【讨论】:

    【解决方案3】:

    我在 server.ts 文件的顶部声明了这一点,它起作用了

    (global as {window: unknown}).window = global;
    

    【讨论】:

    • 如果问题是访问窗口的库/代码正在访问属性,这将不起作用。 window.getComputedStyle() 仍然失败
    【解决方案4】:

    我有 2 个配置相同的 Angular SSR 项目,但其中一个遇到问题,因为 node_modules 使用 window 但未验证。

    解决办法:

    • main.js中查找哪个lib出错,之后再搜索如何通过。

    • 对我来说,我只是将 Nodejs 版本从 10.x 升级到 14.x,问题就解决了。

    【讨论】:

      猜你喜欢
      • 2020-11-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-05
      • 2020-09-16
      • 2020-02-15
      相关资源
      最近更新 更多