Angular12执行serve:ssr报错ReferenceError: window is not defined如何解决?
问题核心原因&对应解决方案
1. 首要修复:调整global挂载逻辑的执行顺序
你当前server.ts里的代码顺序完全错误:所有import导入语句在JavaScript/TypeScript中会被自动提升到文件最顶部执行,你现在是先导入了AppServerModule等业务模块,之后才给global挂载window对象。如果模块顶层代码(包括你依赖的第三方包的顶层代码)存在对window的直接调用,导入时就会触发报错,后面的挂载逻辑完全不会生效。
修复步骤:
- 新增
src/global-patch.ts文件,把你当前所有domino初始化、global属性挂载的逻辑全部移到这个文件中:
const domino = require('domino'); const fs = require('fs'); const path = require('path'); const distFolder = path.join(process.cwd(), 'dist/treo/browser'); const template = fs.readFileSync(path.join(distFolder, 'index.html')).toString(); const win = domino.createWindow(template); (global as any).window = win; (global as any).document = win.document; (global as any).DOMTokenList = win.DOMTokenList; (global as any).Node = win.Node; (global as any).Text = win.Text; (global as any).HTMLElement = win.HTMLElement; (global as any).navigator = win.navigator; (global as any).WebSocket = require('ws'); (global as any).XMLHttpRequest = require('xhr2');
- 在
server.ts的第一行引入这个补丁文件,再写其他导入逻辑:
import './src/global-patch'; // 后面再写其他所有import代码 import { ngExpressEngine } from '@nguniversal/express-engine'; // ...剩余原有逻辑
2. 排查第三方依赖的window调用
你自己的业务代码没有直接调用window,不代表第三方依赖没有这类逻辑:很多前端库(比如图表库、UI组件、工具库)会直接在顶层作用域访问window,不会等到运行时再判断环境。
修复方案:
- 开启服务端调试定位报错源:修改
package.json里的serve:ssr命令,加调试参数:node --inspect dist/treo/server/main.js,打开Chrome访问chrome://inspect即可抓取完整报错栈,直接定位是哪个依赖触发的问题。 - 对触发问题的依赖做特殊处理:
- 如果是你业务代码中引入的依赖,不要在文件顶部静态导入,改用动态导入仅在浏览器环境加载:
if(isPlatformBrowser(this.platformId)) { import('xxx-library').then(lib => { // 执行相关逻辑 }) }- 如果是组件库的组件触发问题,在服务端模块(
app.server.module.ts)中用空占位组件替换该组件,避免服务端渲染时执行。
3. 检查业务代码的顶层window调用
排查你所有业务代码文件中,是否存在不在类/函数内部的顶层window调用,比如:
// 这种顶层代码导入时就会执行,服务端直接报错 const screenWidth = window.innerWidth; @Component({ // 装饰器内如果用到window也会直接报错 host: { '[style.width.px]': window.innerWidth } })
这类逻辑要全部移入方法内部,包裹isPlatformBrowser判断。
4. 补全WindowRef的服务端实现
你当前的WindowRef服务默认可能仅在浏览器环境返回正确对象,需要在app.server.module.ts的providers数组中新增服务端的实现:
providers: [ { provide: WindowRef, useValue: { nativeWindow: (global as any).window } } ]
内容的提问来源于stack exchange,提问作者Santosh
相关产品推荐
相关产品推荐

