Angular 6 SSR集成ng-bootstrap报错:ReferenceError: Document is not defined
Document is not defined错误 这个报错的本质很明确:服务端渲染(SSR)是在Node.js环境中运行的,这里没有浏览器的Document对象,而你用的ng-bootstrap 2.0.0版本中,NgbFocusTrapFactory这个类在初始化时直接依赖了浏览器的DOM API,导致服务端执行代码时找不到Document,抛出了ReferenceError。
下面给你几个可行的解决方案,按推荐优先级排序:
1. 为服务端提供NgbFocusTrapFactory的替代实现
既然服务端不需要焦点陷阱(焦点管理是浏览器端的交互需求),我们可以创建一个服务端专用的工厂类,覆盖原有的实现,避免在服务端访问Document:
首先,在你的服务端模块(比如app.server.module.ts)所在目录新建一个server-focus-trap.factory.ts文件,内容如下:
import { Injectable, PLATFORM_ID, Inject } from '@angular/core'; import { isPlatformServer } from '@angular/common'; import { NgbFocusTrapFactory } from '@ng-bootstrap/ng-bootstrap'; @Injectable() export class ServerFocusTrapFactory extends NgbFocusTrapFactory { constructor(@Inject(PLATFORM_ID) private platformId: Object) { super(); } create(element: HTMLElement) { // 如果是服务端环境,返回空的焦点陷阱实现 if (isPlatformServer(this.platformId)) { return { trap: () => {}, release: () => {}, destroy: () => {} }; } // 浏览器环境使用原有的实现 return super.create(element); } }
然后在app.server.module.ts的providers数组中替换原有的NgbFocusTrapFactory:
import { NgModule } from '@angular/core'; import { ServerModule } from '@angular/platform-server'; import { AppModule } from './app.module'; import { AppComponent } from './app.component'; import { ServerFocusTrapFactory } from './server-focus-trap.factory'; import { NgbFocusTrapFactory } from '@ng-bootstrap/ng-bootstrap'; @NgModule({ imports: [AppModule, ServerModule], bootstrap: [AppComponent], providers: [ { provide: NgbFocusTrapFactory, useClass: ServerFocusTrapFactory } ] }) export class AppServerModule {}
2. 检查并避免在服务端直接访问DOM API
除了ng-bootstrap的问题,你还要排查自己的代码中有没有在服务端执行的逻辑(比如组件构造函数、ngOnInit等生命周期钩子)中直接访问document或window。如果有,一定要用Angular提供的PLATFORM_ID和isPlatformBrowser/isPlatformServer工具函数做环境判断:
import { Component, Inject, PLATFORM_ID } from '@angular/core'; import { isPlatformBrowser } from '@angular/common'; @Component({...}) export class YourComponent { constructor(@Inject(PLATFORM_ID) private platformId: Object) {} ngOnInit() { if (isPlatformBrowser(this.platformId)) { // 只有浏览器环境才执行DOM相关操作 document.getElementById('some-element').style.display = 'block'; } } }
3. 考虑升级ng-bootstrap版本(可选)
ng-bootstrap后续版本(比如3.x及以上)对SSR的兼容性更好,如果你能升级Angular和ng-bootstrap的版本(注意版本匹配:Angular 6对应ng-bootstrap 2.x,Angular 7对应3.x),可能会直接解决这个问题。不过升级前要做好依赖兼容性测试,避免引入新的问题。
内容的提问来源于stack exchange,提问作者Hadi Rasouli

