Angular 18项目出现[vite] /home页面30秒未渲染内部服务器错误
Angular 18 + Vite 随机出现「Page /home did not render in 30 seconds」错误排查
错误日志
9:20:15 PM [vite] Internal server error: Page /home did not render in 30 seconds. at Timeout.<anonymous> (/Users/jan/Projekts/PflanzenApp/node_modules/@angular/build/src/utils/server-rendering/render-page.js:90:90) at Timeout.timer (/Users/jan/Projekts/PflanzenApp/node_modules/zone.js/fesm2015/zone-node.js:2198:37) at _ZoneDelegate.invokeTask (/Users/jan/Projekts/PflanzenApp/node_modules/zone.js/fesm2015/zone-node.js:398:33) at _ZoneImpl.runTask (/Users/jan/Projekts/PflanzenApp/node_modules/zone.js/fesm2015/zone-node.js:158:47) at invokeTask (/Users/jan/Projekts/PflanzenApp/node_modules/zone.js/fesm2015/zone-node.js:479:34) at Timeout.ZoneTask.invoke (/Users/jan/Projekts/PflanzenApp/node_modules/zone.js/fesm2015/zone-node.js:468:48) at Timeout.data.args.<computed> (/Users/jan/Projekts/PflanzenApp/node_modules/zone.js/fesm2015/zone-node.js:2180:32) at listOnTimeout (node:internal/timers:573:17) at process.processTimers (node:internal/timers:514:7)
问题根源分析
从你的HomePageComponent代码来看,错误的核心原因是SSR(服务器端渲染)环境下未正确处理定时器:
ngOnInit中启动的setInterval会在服务器端持续运行,且不会随页面渲染完成自动清除,导致服务器进程资源被持续占用,当并发请求或资源不足时就会触发渲染超时。checkWatering方法每秒更新组件的days/hours等全局属性,触发频繁的Angular变更检测,进一步加重服务器渲染线程的负载。- 额外bug:当前代码中多个固定植物的倒计时会互相覆盖(最后一个植物的倒计时会覆盖之前的),因为这些属性是组件级别的,而非每个植物独立持有。
修复方案
1. 区分环境启动定时器
仅在浏览器环境中启动定时器,避免SSR进程被无意义的定时器占用:
import { Component, OnInit, OnDestroy, Inject, PLATFORM_ID } from '@angular/core'; import { isPlatformBrowser } from '@angular/common'; // 其他导入保持不变 export class HomePageComponent implements OnInit, OnDestroy { plants: Plant[] = []; pinnedPlants: Plant[] = []; private intervalId: number | null = null; // 保存定时器ID用于销毁 constructor( private plantService: PlantService, @Inject(PLATFORM_ID) private platformId: Object // 注入平台标识 ) { this.plants = plantService.getOwnedPlants(); } ngOnInit(): void { this.plants = this.plantService.getOwnedPlants(); this.pinnedPlants = this.plants.filter(plant => plant.pinned); this.pinnedPlants.forEach(plant => this.updatePinnedPlant(plant)); // 仅在浏览器环境启动定时器 if (isPlatformBrowser(this.platformId)) { this.intervalId = window.setInterval(() => this.checkWatering(), 1000); } } // 组件销毁时清除定时器,避免内存泄漏 ngOnDestroy(): void { if (this.intervalId) { clearInterval(this.intervalId); } } // 其他方法保持不变... }
2. 优化倒计时数据结构
让每个植物独立持有倒计时属性,避免全局属性覆盖和不必要的变更检测:
- 先修改
Plant类,添加倒计时相关字段:
export class Plant { // 原有属性(如pinned、waterTime、maintenance等) days: number = 0; hours: number = 0; minutes: number = 0; seconds: number = 0; waterAmount: number = 0; }
- 然后修改
checkWatering方法:
checkWatering(): void { const currentTime = new Date(); this.pinnedPlants.forEach(plant => { let timeTillWater = plant.waterTime.getTime() - currentTime.getTime(); // 更新当前植物的倒计时属性 plant.days = Math.floor(timeTillWater / (24 * 60 * 60 * 1000)); plant.hours = Math.floor((timeTillWater % (24 * 60 * 60 * 1000)) / (60 * 60 * 1000)); plant.minutes = Math.floor((timeTillWater % (60 * 60 * 1000)) / (60 * 1000)); plant.seconds = Math.floor((timeTillWater % (60 * 1000)) / 1000); let multiplier = 1; switch (plant.maintenance) { case 'Monthly': multiplier = 30; break; case 'Weekly': multiplier = 7; break; case 'Medium': case 'Daily': default: multiplier = 1; } plant.waterAmount = plant.water * multiplier; if (timeTillWater <= 0) { console.log("Gießen") } }); }
3. 可选:启用OnPush变更检测策略
进一步减少变更检测频率,提升性能:
@Component({ selector: 'app-home-page', standalone: true, imports: [QuoteComponent, RouterLink, WeatherDisplayComponent, CommonModule, NgIf], templateUrl: './home-page.component.html', styleUrl: './home-page.component.css', changeDetection: ChangeDetectionStrategy.OnPush // 添加这一行 })
内容的提问来源于stack exchange,提问作者jamakr4
相关产品推荐
相关产品推荐

