Angular Universal SSR注入Document报错:Document is not defined
这个报错确实和Angular Universal的服务端加载机制有关,但核心原因并非导入顺序,而是服务端环境中不存在全局Document对象,你的组件/依赖代码在打包时被webpack解析为直接引用全局Document类型。
为什么直接在AppComponent注入没问题?
AppComponent作为根组件,Angular Universal的服务端渲染初始化流程会优先处理根注入器的DOCUMENT令牌,注入由@angular/platform-server提供的模拟Document对象。而封装在子模块的组件,其依赖元数据在模块被导入时就会被webpack静态分析,若代码中存在对Document类型的直接引用(哪怕是类型标注),webpack可能会将其编译为对全局Document变量的访问,而服务端(Node.js)环境中并没有这个全局变量,从而触发报错。
解决方法
确保所有
Document相关操作都通过注入令牌获取
你的组件已经用@Inject(DOCUMENT)注入了Document,但要检查BlockScrollStrategy的实现是否直接依赖全局Document类型。如果是自定义实现,需修改为通过参数接收注入的Document实例,而非直接引用全局Document。改用Angular CDK内置的兼容API
避免手动实例化滚动策略,使用Angular CDK的Overlay服务提供的滚动策略工厂,这些API已内置SSR兼容处理:import { Overlay } from '@angular/cdk/overlay'; constructor(private overlay: Overlay) {} open() { const scrollStrategy = this.shouldBlockScroll ? this.overlay.scrollStrategies.block() : this.overlay.scrollStrategies.noop(); }模块层面配置SSR兼容的
DOCUMENT提供器
在OverlayModule中添加服务端适配的DOCUMENT提供逻辑,确保服务端环境能获取模拟的Document对象:import { NgModule, PLATFORM_ID } from '@angular/core'; import { CommonModule, isPlatformServer } from '@angular/common'; import { OverlayComponent } from './overlay.component'; import { DOCUMENT } from '@angular/common'; @NgModule({ imports: [CommonModule], declarations: [OverlayComponent], exports: [OverlayComponent], providers: [ { provide: DOCUMENT, useFactory: (platformId: object) => { if (isPlatformServer(platformId)) { return require('domino').createWindow().document; } return document; }, deps: [PLATFORM_ID] } ] }) export class OverlayModule {}注:如果你的Nx工作区已配置Angular Universal的domino环境,可直接复用项目中已有的服务端Document模拟逻辑。
优化webpack打包配置
在项目的服务端构建配置中,阻止webpack将Document解析为全局变量。在project.json的server构建目标中添加额外webpack配置:"build": { "executor": "@angular-devkit/build-angular:server", "options": { "outputPath": "dist/apps/project/server", "main": "apps/project/src/main.server.ts", "tsConfig": "apps/project/tsconfig.server.json", "extraWebpackConfig": "apps/project/webpack.server.config.js" } }新建
webpack.server.config.js文件并添加:module.exports = { externals: { document: 'commonjs document' } };
内容的提问来源于stack exchange,提问作者Oriol Miró

