Angular SSR使用jsPlumb等第三方库时出现document未定义错误
问题原因
Angular SSR 运行在 Node.js 服务端环境,该环境不存在浏览器提供的 document、window 等全局 API。你用到的 jsPlumb、@jsplumb/browser-ui、tinymce、html2canvas 这类前端UI库,底层都强依赖浏览器API,服务端执行相关初始化/调用逻辑时就会抛出该错误。
构建时无报错是因为打包过程仅做代码转译,不会实际执行调用浏览器API的运行时代码。
解决方案
方案1:仅在浏览器端加载/初始化相关库(优先推荐)
利用 Angular 提供的 PLATFORM_ID 令牌判断当前运行环境,仅在浏览器环境执行依赖浏览器API的逻辑:
- 导入环境判断工具:
import { PLATFORM_ID, Inject } from '@angular/core'; import { isPlatformBrowser } from '@angular/common';
- 在组件/服务中注入
PLATFORM_ID,判断环境后再执行相关逻辑:
export class YourComponent { isBrowser: boolean; constructor(@Inject(PLATFORM_ID) platformId: Object) { this.isBrowser = isPlatformBrowser(platformId); } ngOnInit() { if (this.isBrowser) { // 只有浏览器端才初始化jsPlumb等依赖浏览器的库 import('jsplumb').then(jsPlumbModule => { // 这里写jsPlumb的初始化逻辑 }) // 其他依赖浏览器的逻辑也都放在这个判断分支内 } } }
该方案可以从根源上避免服务端执行浏览器端专属代码,也不会产生额外的兼容问题。
方案2:服务端Mock浏览器全局API
如果不想改业务代码,可以在服务端入口文件 server.ts 顶部 mock 缺少的浏览器全局对象:
首先安装依赖:npm i domino
然后在 server.ts 最开头添加如下代码:
const domino = require('domino'); const fs = require('fs'); const path = require('path'); // 读取客户端构建出来的index.html作为模板 const template = fs.readFileSync(path.join(process.cwd(), 'dist/newadmin/browser/index.html')).toString(); const win = domino.createWindow(template); // 挂载到全局对象 global['window'] = win; global['document'] = win.document; global['navigator'] = win.navigator; // 其他你用到的浏览器全局对象都可以在这里按需mock
该方案属于兼容方案,部分复杂库可能会出现行为和浏览器不一致的问题,适合临时快速修复。
额外注意事项
你项目的package.json中同时安装了 jsplumb@2.15.6 和 @jsplumb/browser-ui@5.0.6 两个版本的jsPlumb,存在版本冲突,建议统一使用一个版本,避免出现未知的运行时错误。
内容的提问来源于stack exchange,提问作者sreenath
相关产品推荐
相关产品推荐

