You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular 7 Universal SSR部署Heroku遇document未定义错误致应用崩溃求助

Angular SSR on Heroku: Fixing ERROR ReferenceError: document is not defined & App Crashes

Let's break down what's going on here and how to fix it—this is a super common pitfall with Angular SSR, especially when deploying to platforms like Heroku.

Why This Happens

Your isPlatformBrowser check is a good start, but two key issues are likely tripping you up:

  1. Early Injection of DOCUMENT: If you’re directly injecting DOCUMENT into your component (like @Inject(DOCUMENT) private document: Document), Angular tries to resolve this dependency before your platform check runs. On the server, there’s no document object, so this throws an error immediately—your conditional never even gets a chance to execute.
  2. Uncaught Errors Causing Heroku Crashes: Every ReferenceError piles up in your logs, and uncaught exceptions will eventually take down your Node.js process on Heroku. That’s why you’re seeing the "Application error" after some time.

Step-by-Step Fixes

1. Safe Injection of DOCUMENT

Instead of injecting DOCUMENT directly, create a factory provider that returns a safe mock for the server. Add this to your app’s root providers (e.g., app.module.ts or app.server.module.ts):

import { NgModule } from '@angular/core';
import { PLATFORM_ID, DOCUMENT } from '@angular/common';
import { isPlatformBrowser } from '@angular/common';

@NgModule({
  providers: [
    {
      provide: DOCUMENT,
      useFactory: (platformId: Object) => {
        // Return real document if in browser, else a harmless mock
        return isPlatformBrowser(platformId) ? document : {} as Document;
      },
      deps: [PLATFORM_ID]
    }
  ]
})
export class AppModule {}

This ensures the server gets a mock object instead of trying to access a non-existent document.

2. Add DOM Simulation to Server-Side Code

To fully avoid document-related errors during SSR, use domino to mock the browser’s DOM environment in your server.ts file. First install the package:

npm install domino --save-dev

Then update server.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');

// Load browser index.html to create a mock window
const template = fs.readFileSync(join(process.cwd(), 'dist/browser/index.html')).toString();
const win = domino.createWindow(template);

// Mock browser globals for server-side rendering
global['window'] = win;
global['document'] = win.document;
global['navigator'] = win.navigator;

// Rest of your server.ts code...

This gives the server a fake DOM to work with, so any accidental access to document won’t throw errors.

3. Guard Document Operations Properly

Even with the above fixes, make sure your document-related code is wrapped both in the platform check and only runs after the component has initialized in the browser. Move your style-setting code to ngAfterViewInit (which only runs when the DOM is ready):

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

@Component({
  selector: 'app-your-component',
  templateUrl: './your-component.component.html'
})
export class YourComponent implements AfterViewInit {
  isMenuOpened = false;

  constructor(
    @Inject(PLATFORM_ID) private platformId: Object,
    private renderer: Renderer2,
    @Inject(DOCUMENT) private document: Document
  ) {}

  ngAfterViewInit(): void {
    if (isPlatformBrowser(this.platformId)) {
      const overflow = this.isMenuOpened ? 'hidden' : 'auto';
      this.renderer.setStyle(this.document.body, 'overflow', overflow);
    }
  }
}

4. Prevent Uncaught Exceptions from Crashing Heroku

Add error handlers to your server.ts to catch uncaught exceptions and keep your process running (or exit gracefully):

// At the top of server.ts, after imports
process.on('uncaughtException', (err) => {
  console.error('Uncaught Exception:', err);
  // Optional: Exit gracefully if needed
  // process.exit(1);
});

process.on('unhandledRejection', (reason, promise) => {
  console.error('Unhandled Rejection at:', promise, 'Reason:', reason);
});

This stops minor errors from snowballing into a full app crash on Heroku.

5. Verify Your Build Configuration

Double-check your webpack.server.config.js to ensure it’s properly excluding browser-only dependencies. Make sure it has something like:

module.exports = {
  target: 'node',
  externals: [
    // Exclude node modules that don't work in Node.js
    /node_modules/,
  ],
  // Rest of your config...
};

Final Checks

After making these changes:

  1. Test SSR locally first with npm run build && npm start to confirm no document errors appear in your logs.
  2. Deploy to Heroku again, then monitor logs with heroku logs --tail to verify the errors are gone.
  3. If you’re using third-party libraries, ensure they’re SSR-compatible—some libraries might hardcode document access, so you may need to dynamically import them only in the browser.

内容的提问来源于stack exchange,提问作者Michael Kutateladze

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 04:39:29