"window is not defined" 在 Angular 服务中,但代码运行良好
"window is not defined" in Angular service yet code works perfectly
我在 Angular 8 服务中使用 indexeddb 并且需要 window
。代码构建没有错误,应用程序完美地创建了数据库对象存储。但是在 运行 生产模式下(使用实际的节点服务器而不是 ng serve 时不会发生此错误),我在终端 运行ning angular:
ERROR ReferenceError: window is not defined
at IndexedDBService.isSupported (D:\MartijnFiles\Documents\Programming\Fenego\fenego-labs-angular\dist\server.js:71199:9)
at IndexedDBService.openDB (D:\MartijnFiles\Documents\Programming\Fenego\fenego-labs-angular\dist\server.js:71203:18)
at Promise (D:\MartijnFiles\Documents\Programming\Fenego\fenego-labs-angular\dist\server.js:72026:46)
同样,一切正常,如果 window
实际上未定义,isSupported()
函数将阻止 openDB()
变为 运行。浏览器控制台也没有报错
这是我服务的相关部分。
@Injectable()
export class IndexedDBService {
isSupported(): boolean {
return !!window.indexedDB;
}
openDB(dbName: string,
version: number,
onUpgradeNeededCallback: OnUpgradeNeededCallback,
onSuccessCallback: OnOpenSuccessCallback,
onErrorCallback: OnOpenErrorCallback,
onBlockedCallback: OnOpenBlockedCallback): Observable<IDBOpenDBRequest> {
let openDBRequest: IDBOpenDBRequest = null;
if (this.isSupported()) {
openDBRequest = window.indexedDB.open(dbName, version);
openDBRequest.onupgradeneeded = onUpgradeNeededCallback;
openDBRequest.onsuccess = onSuccessCallback;
openDBRequest.onerror = onErrorCallback;
openDBRequest.onblocked = onBlockedCallback;
}
return of(openDBRequest);
}
有很多建议 "solutions" 主要归结为通过服务或普通注入提供它(例如本博客中的第 1 点 https://willtaylor.blog/angular-universal-gotchas/),但它所做的只是通过 window 通过注入到我的一些其他服务。但是我的代码有效,所以它显然可以访问 window...
更新:
组件 ngOnInit()
中的以下行与 Worker 存在相同的问题 "not defined" 但 worker 已加载并且 运行s 完美:
const offlineProductsWorker = new Worker('webworkers/offline-products-worker.js');
更新2:
我找到了一个解决方案(在下面发布)但是检查服务器端渲染似乎更像是一种解决方法,而不是解决服务器端渲染正在发生的事实(不确定是否应该是这种情况)。
我将在下面包含我与 webpack 一起使用的 server.ts 脚本。这是对另一个项目的一个修改,我不明白其中的大部分内容。如果有人能向我指出我可以更改哪些内容以停止服务器端渲染,那就太好了。或者,如果它应该这样做,那为什么?
// tslint:disable:ish-ordered-imports no-console
import 'reflect-metadata';
import 'zone.js/dist/zone-node';
import { enableProdMode } from '@angular/core';
import * as express from 'express';
import { join } from 'path';
import * as https from 'https';
import * as fs from 'fs';
/*
* Load config from .env file
*/
require('dotenv').config({ path: './ng-exp/.env' });
const IS_HTTPS = process.env.IS_HTTPS === 'true';
const SSL_PATH = process.env.SSL_PATH;
const ENVIRONMENT = process.env.ENVIRONMENT;
// Faster server renders w/ Prod mode (dev mode never needed)
enableProdMode();
const logging = !!process.env.LOGGING;
// Express server
const app = express();
const PORT = process.env.PORT || 4200;
const DIST_FOLDER = process.cwd();
// * NOTE :: leave this as require() since this file is built Dynamically from webpack
const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist/server/main');
// Express Engine
import { ngExpressEngine } from '@nguniversal/express-engine';
// Import module map for lazy loading
import { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader';
// Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
app.engine(
'html',
ngExpressEngine({
bootstrap: AppServerModuleNgFactory,
providers: [provideModuleMap(LAZY_MODULE_MAP)],
})
);
app.set('view engine', 'html');
app.set('views', join(DIST_FOLDER, 'ng-exp'));
// Server static files from /browser
app.get(
'*.*',
express.static(join(DIST_FOLDER, 'ng-exp'), {
setHeaders: (res, path) => {
if (/\.[0-9a-f]{20,}\./.test(path)) {
// file was output-hashed -> 1y
res.set('Cache-Control', 'public, max-age=31557600');
} else {
// file should be re-checked more frequently -> 5m
res.set('Cache-Control', 'public, max-age=300');
}
},
})
);
// ALl regular routes use the Universal engine
app.get('*', (req: express.Request, res: express.Response) => {
if (logging) {
console.log(`GET ${req.url}`);
}
res.render(
'index',
{
req,
res,
},
(err: Error, html: string) => {
res.status(html ? res.statusCode : 500).send(html || err.message);
if (logging) {
console.log(`RES ${res.statusCode} ${req.url}`);
if (err) {
console.log(err);
}
}
}
);
});
const sslOptions = {
key: fs.readFileSync(`${SSL_PATH}/${ENVIRONMENT}/server.key`),
cert: fs.readFileSync(`${SSL_PATH}/${ENVIRONMENT}/server.crt`),
};
// Start up the Node server
let server;
if (IS_HTTPS) {
server = https.createServer(sslOptions, app);
} else {
server = app;
}
server.listen(PORT, () => {
console.log(`Node Express server listening on http${IS_HTTPS ? 's' : ''}://localhost:${PORT}`);
const icmBaseUrl = process.env.ICM_BASE_URL;
if (icmBaseUrl) {
console.log('ICM_BASE_URL is', icmBaseUrl);
}
});
这里有一个相关的问题:
https://github.com/hellosign/hellosign-embedded/issues/107
基本上,为了避免错误,您可以在全局某处声明 window。
if (typeof window === 'undefined') {
global.window = {}
}
我还发现 更好地解释了问题以及为什么它在客户端有效。
感谢 ChrisY 的一些输入,我找到了解决方案
我使用 webpack 部署代码,运行 使用 node.js 部署代码。似乎节点以某种方式在服务器端呈现它,然后浏览器也呈现它。服务器站点部分对店面没有影响,但会导致(看似无害的)错误。在 isSupported()
中,我添加了 console.log(isPlatformBrowser(this.platformId))
,它在服务器终端中打印为 false,但在浏览器中为 true。因此,我将代码更改如下:
constructor(@Inject(PLATFORM_ID) private platformId: any) {}
isSupported(): boolean {
return isPlatformBrowser(this.platformId) && !!indexedDB;
}
现在它仍然像以前一样在浏览器中工作,但没有服务器错误。
更新:
服务器端渲染的原因我也找到了。描述中的 server.ts
文件有一个带有 res.render(
的块。这首先在服务器上呈现页面,如果它没有收到 html,它会收到 returns 状态代码 500。否则它允许客户端呈现它。鉴于这是一个现实的场景,我决定在我的代码中保留额外的 isPlatformBrowser(this.platformId)
检查。然后应该对只能由客户执行的任何事情(window、dom、工作人员等)执行此操作。
不是没有服务器端呈现,res.render(
块的替代方法是
res.status(200).sendFile(`/`, {root: join(DIST_FOLDER, 'ng-exp')});
我在 Angular 8 服务中使用 indexeddb 并且需要 window
。代码构建没有错误,应用程序完美地创建了数据库对象存储。但是在 运行 生产模式下(使用实际的节点服务器而不是 ng serve 时不会发生此错误),我在终端 运行ning angular:
ERROR ReferenceError: window is not defined
at IndexedDBService.isSupported (D:\MartijnFiles\Documents\Programming\Fenego\fenego-labs-angular\dist\server.js:71199:9)
at IndexedDBService.openDB (D:\MartijnFiles\Documents\Programming\Fenego\fenego-labs-angular\dist\server.js:71203:18)
at Promise (D:\MartijnFiles\Documents\Programming\Fenego\fenego-labs-angular\dist\server.js:72026:46)
同样,一切正常,如果 window
实际上未定义,isSupported()
函数将阻止 openDB()
变为 运行。浏览器控制台也没有报错
这是我服务的相关部分。
@Injectable()
export class IndexedDBService {
isSupported(): boolean {
return !!window.indexedDB;
}
openDB(dbName: string,
version: number,
onUpgradeNeededCallback: OnUpgradeNeededCallback,
onSuccessCallback: OnOpenSuccessCallback,
onErrorCallback: OnOpenErrorCallback,
onBlockedCallback: OnOpenBlockedCallback): Observable<IDBOpenDBRequest> {
let openDBRequest: IDBOpenDBRequest = null;
if (this.isSupported()) {
openDBRequest = window.indexedDB.open(dbName, version);
openDBRequest.onupgradeneeded = onUpgradeNeededCallback;
openDBRequest.onsuccess = onSuccessCallback;
openDBRequest.onerror = onErrorCallback;
openDBRequest.onblocked = onBlockedCallback;
}
return of(openDBRequest);
}
有很多建议 "solutions" 主要归结为通过服务或普通注入提供它(例如本博客中的第 1 点 https://willtaylor.blog/angular-universal-gotchas/),但它所做的只是通过 window 通过注入到我的一些其他服务。但是我的代码有效,所以它显然可以访问 window...
更新:
组件 ngOnInit()
中的以下行与 Worker 存在相同的问题 "not defined" 但 worker 已加载并且 运行s 完美:
const offlineProductsWorker = new Worker('webworkers/offline-products-worker.js');
更新2:
我找到了一个解决方案(在下面发布)但是检查服务器端渲染似乎更像是一种解决方法,而不是解决服务器端渲染正在发生的事实(不确定是否应该是这种情况)。
我将在下面包含我与 webpack 一起使用的 server.ts 脚本。这是对另一个项目的一个修改,我不明白其中的大部分内容。如果有人能向我指出我可以更改哪些内容以停止服务器端渲染,那就太好了。或者,如果它应该这样做,那为什么?
// tslint:disable:ish-ordered-imports no-console
import 'reflect-metadata';
import 'zone.js/dist/zone-node';
import { enableProdMode } from '@angular/core';
import * as express from 'express';
import { join } from 'path';
import * as https from 'https';
import * as fs from 'fs';
/*
* Load config from .env file
*/
require('dotenv').config({ path: './ng-exp/.env' });
const IS_HTTPS = process.env.IS_HTTPS === 'true';
const SSL_PATH = process.env.SSL_PATH;
const ENVIRONMENT = process.env.ENVIRONMENT;
// Faster server renders w/ Prod mode (dev mode never needed)
enableProdMode();
const logging = !!process.env.LOGGING;
// Express server
const app = express();
const PORT = process.env.PORT || 4200;
const DIST_FOLDER = process.cwd();
// * NOTE :: leave this as require() since this file is built Dynamically from webpack
const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist/server/main');
// Express Engine
import { ngExpressEngine } from '@nguniversal/express-engine';
// Import module map for lazy loading
import { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader';
// Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
app.engine(
'html',
ngExpressEngine({
bootstrap: AppServerModuleNgFactory,
providers: [provideModuleMap(LAZY_MODULE_MAP)],
})
);
app.set('view engine', 'html');
app.set('views', join(DIST_FOLDER, 'ng-exp'));
// Server static files from /browser
app.get(
'*.*',
express.static(join(DIST_FOLDER, 'ng-exp'), {
setHeaders: (res, path) => {
if (/\.[0-9a-f]{20,}\./.test(path)) {
// file was output-hashed -> 1y
res.set('Cache-Control', 'public, max-age=31557600');
} else {
// file should be re-checked more frequently -> 5m
res.set('Cache-Control', 'public, max-age=300');
}
},
})
);
// ALl regular routes use the Universal engine
app.get('*', (req: express.Request, res: express.Response) => {
if (logging) {
console.log(`GET ${req.url}`);
}
res.render(
'index',
{
req,
res,
},
(err: Error, html: string) => {
res.status(html ? res.statusCode : 500).send(html || err.message);
if (logging) {
console.log(`RES ${res.statusCode} ${req.url}`);
if (err) {
console.log(err);
}
}
}
);
});
const sslOptions = {
key: fs.readFileSync(`${SSL_PATH}/${ENVIRONMENT}/server.key`),
cert: fs.readFileSync(`${SSL_PATH}/${ENVIRONMENT}/server.crt`),
};
// Start up the Node server
let server;
if (IS_HTTPS) {
server = https.createServer(sslOptions, app);
} else {
server = app;
}
server.listen(PORT, () => {
console.log(`Node Express server listening on http${IS_HTTPS ? 's' : ''}://localhost:${PORT}`);
const icmBaseUrl = process.env.ICM_BASE_URL;
if (icmBaseUrl) {
console.log('ICM_BASE_URL is', icmBaseUrl);
}
});
这里有一个相关的问题: https://github.com/hellosign/hellosign-embedded/issues/107 基本上,为了避免错误,您可以在全局某处声明 window。
if (typeof window === 'undefined') {
global.window = {}
}
我还发现
感谢 ChrisY 的一些输入,我找到了解决方案
我使用 webpack 部署代码,运行 使用 node.js 部署代码。似乎节点以某种方式在服务器端呈现它,然后浏览器也呈现它。服务器站点部分对店面没有影响,但会导致(看似无害的)错误。在 isSupported()
中,我添加了 console.log(isPlatformBrowser(this.platformId))
,它在服务器终端中打印为 false,但在浏览器中为 true。因此,我将代码更改如下:
constructor(@Inject(PLATFORM_ID) private platformId: any) {}
isSupported(): boolean {
return isPlatformBrowser(this.platformId) && !!indexedDB;
}
现在它仍然像以前一样在浏览器中工作,但没有服务器错误。
更新:
服务器端渲染的原因我也找到了。描述中的 server.ts
文件有一个带有 res.render(
的块。这首先在服务器上呈现页面,如果它没有收到 html,它会收到 returns 状态代码 500。否则它允许客户端呈现它。鉴于这是一个现实的场景,我决定在我的代码中保留额外的 isPlatformBrowser(this.platformId)
检查。然后应该对只能由客户执行的任何事情(window、dom、工作人员等)执行此操作。
不是没有服务器端呈现,res.render(
块的替代方法是
res.status(200).sendFile(`/`, {root: join(DIST_FOLDER, 'ng-exp')});