Angular 13 SSR构建运行报错:ReferenceError: document is not defined
Angular 13 SSR 报错
document is not defined 修复方案 错误根因
Angular SSR 运行在Node.js服务端环境,该环境不存在浏览器专属的BOM对象(document/window/navigator等),代码中直接调用document.createElement('canvas')时,服务端找不到对应对象就会抛出该引用错误。
错误触发代码与调用栈:
var canvasElement = document.createElement('canvas'); ^
ReferenceError: document is not defined at C:\Users\aakas\Desktop\ArteryUniversal\dist\Universal\server\main.js:64680:25 at C:\Users\aakas\Desktop\ArteryUniversal\dist\Universal\server\main.js:64682:4 at Object.13504 (C:\Users\aakas\Desktop\ArteryUniversal\dist\Universal\server\main.js:64786:3) at __webpack_require__ (C:\Users\aakas\Desktop\ArteryUniversal\dist\Universal\server\main.js:224497:42) at Module.16720 (C:\Users\aakas\Desktop\ArteryUniversal\dist\ArteryUniversal\server\main.js:5796:71) at __webpack_require__ (C:\Users\aakas\Desktop\ArteryUniversal\dist\Universal\server\main.js:224497:42) at Module.90158 (C:\Users\aakas\Desktop\ArteryUniversal\dist\Universal\server\main.js:488:121) at __webpack_require__ (C:\Users\aakas\Desktop\ArteryUniversal\dist\Universal\server\main.js:224497:42) at Module.36747 (C:\Users\aakas\Desktop\ArteryUniversal\dist\Universal\server\main.js:641:77) at __webpack_require__ (C:\Users\aakas\Desktop\ArteryUniversal\dist\Universal\server\main.js:224497:42)
修复方案
按优先级从高到低排列:
方案1:平台判断隔离浏览器专属逻辑(推荐)
Angular 内置了平台识别工具,仅在客户端环境下执行DOM、canvas相关操作,从根源上避免服务端调用不存在的API。
实现步骤:- 注入平台标识,调用
isPlatformBrowser判断运行环境 - 将所有调用
document、操作canvas的逻辑全部放入浏览器环境判断块内
示例代码:
import { Component, Inject, PLATFORM_ID } from '@angular/core'; import { isPlatformBrowser } from '@angular/common'; @Component({ selector: 'app-canvas-demo', template: '' }) export class CanvasDemoComponent { constructor(@Inject(PLATFORM_ID) private platformId: Object) { if (isPlatformBrowser(this.platformId)) { // 仅浏览器端执行 const canvasElement = document.createElement('canvas'); // 后续canvas绘图、第三方canvas库调用逻辑全部写在这里 } } }如果报错来自引入的第三方canvas库,同样在判断块内动态加载库即可,避免服务端打包时执行库内的浏览器API代码。
- 注入平台标识,调用
方案2:使用Angular封装的Renderer2操作DOM
避免直接原生调用document,使用Angular官方提供的Renderer2API操作DOM,框架会自动适配不同运行环境,减少跨平台报错。
示例代码:import { Component, Renderer2, ElementRef, AfterViewInit } from '@angular/core'; import { isPlatformBrowser, PLATFORM_ID } from '@angular/common'; @Component({ selector: 'app-canvas-demo', template: '' }) export class CanvasDemoComponent implements AfterViewInit { constructor( private renderer: Renderer2, private el: ElementRef, @Inject(PLATFORM_ID) private platformId: Object ) {} ngAfterViewInit(): void { // 视图初始化后执行,进一步降低服务端执行概率 if (isPlatformBrowser(this.platformId)) { const canvas = this.renderer.createElement('canvas'); this.renderer.appendChild(this.el.nativeElement, canvas); // 后续canvas操作 } } }注意:Renderer2在服务端仅能生成空DOM节点,无法支持canvas绘图相关API,涉及绘图逻辑依然需要加平台判断。
方案3:服务端注入DOM模拟对象兜底
如果必须在服务端执行DOM相关逻辑,可以在服务端入口server.ts中提前挂载模拟的全局document对象。
实现步骤:- 安装服务端DOM模拟依赖:
npm i domino - 在
server.ts最顶部、启动SSR服务前添加全局模拟配置:
import * as fs from 'fs'; import * as domino from 'domino'; // 替换为你的项目browser端构建产物的index.html路径 const browserIndex = fs.readFileSync('dist/Universal/browser/index.html').toString(); const mockWindow = domino.createWindow(browserIndex); (global as any).window = mockWindow; (global as any).document = mockWindow.document; // 其他用到的BOM对象可按需在此挂载注意:模拟DOM和真实浏览器环境存在差异,canvas绘图、音视频等浏览器专属能力无法在服务端完整模拟,非必要不使用该方案。
- 安装服务端DOM模拟依赖:
内容的提问来源于stack exchange,提问作者Aakash Bhatt
相关产品推荐
相关产品推荐

