【发布时间】:2020-03-19 09:11:29
【问题描述】:
我在 Angular 8 服务中使用 indexeddb,需要 window。代码构建没有错误,应用程序完美地创建了 db objectstore。但是在生产模式下的运行时(使用实际的节点服务器而不是 ng serve 不会发生此错误),我在终端运行 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);
}
那里有许多建议的“解决方案”,主要归结为通过服务或普通注入提供它(例如,本博客中的第 1 点 https://willtaylor.blog/angular-universal-gotchas/)但它所做的只是通过注入从其他一些服务传递窗口到我的。但是我的代码可以正常工作,所以它显然可以访问窗口...
更新:
组件的ngOnInit() 中的以下行与“未定义”的 Worker 存在相同的问题,但该 Worker 已加载并完美运行:
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);
}
});
【问题讨论】:
-
你在做服务器端渲染对吗?根据堆栈跟踪,您实际上陷入了
isSupported()方法。所以我认为这行不通。 -
但它正在工作。我可以在 chrome 开发人员的控制台中看到对象存储,并且每次测试时都会清除完整的 indexedDB。至于服务器端渲染,我不完全确定,但我认为对于网站,Angular 代码提供给浏览器,浏览器完成所有工作。
-
我认为您还应该检查它是否在节点环境中运行。如果是,它应该返回 false。如果有帮助,请参阅this。
-
server.js表示您在此处进行服务器端渲染。因此,当服务器尝试渲染视图时,可能会出现这些错误,因为服务器上没有浏览器和窗口对象。然后所谓的hydrating启动,角代码在客户端执行,然后一切正常。您可能需要添加一些if (isPlatformBrowser())检查以避免此代码在服务器端运行,或者完全禁用服务器端呈现。
标签: node.js angular webpack indexeddb