在Angular Hydration警告消息中添加更多信息,当应用程序在10秒内不稳定时 ```markdown 在Angular Hydration警告消息中添加更多信息,当应用程序在10秒内不稳定时 ```

5f0d552i  于 6个月前  发布在  Angular
关注(0)|答案(8)|浏览(51)

与功能请求相关的@angular/*包有哪些?

  • 无响应*

描述

目前,当你尝试使用Angular进行热更新,而客户端应用在10秒内无法稳定运行时,你会收到一个警告,如下所示:

NG0506: Angular hydration expected the ApplicationRef.isStable() to emit `true`, but it didn't happen within 10000ms. Angular hydration logic depends on the application becoming stable as a signal to complete hydration process. Find more at https://angular.io/errors/NG0506

问题在于,有时很难弄清楚到底是什么原因导致应用程序不稳定。

建议的解决方案

NgZone可以跟踪微任务和宏任务,因此我们可能可以从NgZone中跟踪的内容中获取一些信息,向用户报告到底是什么原因导致应用程序不稳定。

考虑过的替代方案

目前,为了调试这个问题,我要么一直注解掉代码,直到警告消失,以缩小可能导致问题的范围,要么像这个来自@yharaskrik的全局补丁一样:

const o = window.setTimeout;

(window as any)['setTimeout'] = function (handler: TimerHandler, timeout?: number, ...args: any[]) {
    console.trace();
    return o(handler, timeout, ...args);
};

const i = window.setInterval;

(window as any)['setInterval'] = function (handler: TimerHandler, timeout?: number, ...args: any[]) {
    console.trace();
    return i(handler, timeout, ...args);
};
rdlzhqv9

rdlzhqv91#

有一个更简单的方法可以找到是什么阻止了你的应用变得稳定。我遇到了同样的问题,找到了一个旧的技巧来捕获每个正在运行的宏任务。原始脚本在Angular的最新版本中无法正常工作,所以我对其进行了修改(老实说,我已经丢失了原始源代码)。
将这段代码添加到你的app.component.ts文件中:

import { ɵglobal, NgZone } from ‘@angular/core’;

….

private ngZone = inject(NgZone);

constructor() {
const ngZone = ɵglobal.Zone;
const TaskTrackingZone = ngZone.current._parent?._properties?.TaskTrackingZone;

if(!TaskTrackingZone) {
  return;
}

inject(ApplicationRef).isStable.subscribe(stable => {
  this.printNgZone(TaskTrackingZone, 0);
  console.log('Is stable:', stable);
});

this.printNgZone(TaskTrackingZone, 2000);
}

private printNgZone(zone: any, delay: number): void {
  this.ngZone.runOutsideAngular(() => {
    setTimeout(() => {
      // Print to the console all pending tasks
      // (micro tasks, macro tasks and event listeners):
      console.debug('👀 Pending tasks in NgZone: 👀');
      console.debug({
        microTasks: zone.getTasksFor('microTask'),
        macroTasks: zone.getTasksFor('macroTask'),
        eventTasks: zone.getTasksFor('eventTask')
      });

      // Advice how to find the origin of Zone tasks:
      console.debug(
        👀 For every pending Zone Task listed above investigate the stacktrace in the property 'creationLocation' 👆
      );
    }, delay);
  });
}

不要忘记在你的main.ts文件中导入zone.js/dist/task-tracking,也不要忘记在控制台中启用调试输出。祝你好运!

rryofs0p

rryofs0p2#

请不要忘记Angular想要摆脱zone.js,因此您不久将不再需要这个技巧。

xoshrz7s

xoshrz7s3#

作为后续,我最终找到了一些导致应用不稳定的代码片段,但要做到这一点,我不得不取消注解代码并重新测试,直到我缩小到确切的来源。这里有一个:

this.router.events.subscribe({
                next: (val) => {
                    if (val instanceof ActivationEnd) {
                        this.activatedRoute$.next(val.snapshot);
                        this.activatedRoute = val.snapshot;
                    }
                },
                error: (err: any) => {
                    this.log.error(err);
                },
                complete: () => {},
            });

还有一个:

const replayConfig = { maskAllText: false, maskAllInputs: false, blockAllMedia: false };
            const integrations = [new SentryFullStory('gethuman', { client: FullStory }), new Replay(replayConfig)];
            Sentry.init({ dsn: config.sentryUrl, replaysSessionSampleRate: 0.1, replaysOnErrorSampleRate: 1.0, integrations });

这对我来说真的很令人惊讶,因为它肯定是Sentry库中正在进行的一些连续异步操作。
在所有情况下,解决方法都是将有问题的代码 Package 在一个 this.zone.runOutsideAngular() 中。
但我的具体问题并不是这个GitHub issue的重点。这个GitHub issue的目的是尝试添加一些功能,使开发者能够比今天早上我花在注解和取消注解代码上的痛苦小时更容易地调试这个问题。
只要区域仍然存在,我觉得人们会继续遇到这个问题,他们可能需要更多的指导来找出有问题的代码。

pbgvytdp

pbgvytdp4#

作为后续行动,我最终找到了导致应用程序保持不稳定的原因,但要做到这一点,我不得不取消注解代码并重新测试,直到我缩小到确切的来源。对我来说,这最终变成了这段代码:

this.router.events.subscribe({
                next: (val) => {
                    if (val instanceof ActivationEnd) {
                        this.activatedRoute$.next(val.snapshot);
                        this.activatedRoute = val.snapshot;
                    }
                },
                error: (err: any) => {
                    this.log.error(err);
                },
                complete: () => {},
            });

一旦我将这段代码 Package 在一个 this.zone.runOutsideAngular() 中,就不再是问题了。但我的具体根本原因不是这个GitHub问题的重点。这个GitHub问题是尝试添加一些内容,使开发人员能够比今天早上我花在注解和取消注解代码上的痛苦小时更容易调试这个问题。
只要区域仍然存在,我觉得人们会继续遇到这个问题,他们可能需要更多的指导来找出引起问题的代码。
干得好Jeff,我也一直在采取类似的方法来弄清楚发生了什么。
我生成了一个全新的应用程序,并逐渐向其中添加组件以查看哪些部分引起了问题(这给了我一个很好的理由来审计我们无论如何都要包含在根目录中的所有内容,但仍然有很多工作要做来追踪脱水问题哈哈)

n7taea2i

n7taea2i5#

感谢@jeffwhelpley,我也逐个组件地评论,发现在我的案例中,mdb-carousel导致了问题,并用ngb-carousel替换了它。还有一个组件有一个自定义函数调用,我不得不在angular之外运行它。

很难从上面的app.component补丁代码中找出长时间任务,因为其中大多数都是显示来自库代码的调用。

如果angular能在控制台中显示长时间任务的详细信息,以阻碍稳定性,这将使开发人员更容易找出问题。

nkoocmlb

nkoocmlb6#

作为后续,我最终找到了一些导致应用不稳定的代码片段,但要做到这一点,我不得不取消注解代码并重新测试,直到我缩小到确切的来源。这里有一个:

this.router.events.subscribe({
                next: (val) => {
                    if (val instanceof ActivationEnd) {
                        this.activatedRoute$.next(val.snapshot);
                        this.activatedRoute = val.snapshot;
                    }
                },
                error: (err: any) => {
                    this.log.error(err);
                },
                complete: () => {},
            });

还有一个:

const replayConfig = { maskAllText: false, maskAllInputs: false, blockAllMedia: false };
            const integrations = [new SentryFullStory('gethuman', { client: FullStory }), new Replay(replayConfig)];
            Sentry.init({ dsn: config.sentryUrl, replaysSessionSampleRate: 0.1, replaysOnErrorSampleRate: 1.0, integrations });

这对我来说真的很惊讶,因为它肯定是Sentry库中的某个东西在进行一些持续的异步操作。
在所有情况下,解决方法都是将有问题的代码包裹在一个 this.zone.runOutsideAngular() 中。
但我的具体问题并不是这个GitHub issue的重点。这个GitHub issue的目的是尝试添加一些功能,使开发者能够比我今天早上花费的那些痛苦的小时数更容易地调试这个问题。
只要区域仍然存在,我觉得人们会继续遇到这个问题,他们可能需要更多的指导来找出有问题的代码。
我不知道为什么路由器订阅会导致这个问题。你能解释一下吗?
无论如何,我完全支持这个请求。我对这个问题感到抓狂。
使用来自node的任务跟踪,我只能在firestore库中得到一个setTimeout。我怀疑这是firestore内部的事情,因为我确保了我所有的firestore调用都已解析或完成(observables)。
当然,我无法摆脱firestore,也无法在hydration发生后延迟对firestore的调用,因为这会使整个SSR在没有来自dB的信息的情况下变得毫无用处。
顺便说一下,Angular在服务器端变得稳定,否则什么都不会发送到客户端。不仅仅是在客户端。
我真的需要从Angular那里得到一些线索来缩小问题的范围。

cqoc49vn

cqoc49vn7#

有一个更容易的方法来找出是什么阻止了你的应用变得稳定。我遇到了同样的问题,找到了一个旧的技巧来捕获每个正在运行的宏任务。原始脚本在angular的最新版本中无法正常工作,所以我修改了它(说实话,我丢失了原始源代码)。
将这段代码添加到你的app.component.ts文件中:

import { ɵglobal, NgZone } from ‘@angular/core’;

….

private ngZone = inject(NgZone);

constructor() {
const ngZone = ɵglobal.Zone;
const TaskTrackingZone = ngZone.current._parent?._properties?.TaskTrackingZone;

if(!TaskTrackingZone) {
  return;
}

inject(ApplicationRef).isStable.subscribe(stable => {
  this.printNgZone(TaskTrackingZone, 0);
  console.log('Is stable:', stable);
});

this.printNgZone(TaskTrackingZone, 2000);
}

private printNgZone(zone: any, delay: number): void {
  this.ngZone.runOutsideAngular(() => {
    setTimeout(() => {
      // Print to the console all pending tasks
      // (micro tasks, macro tasks and event listeners):
      console.debug('👀 Pending tasks in NgZone: 👀');
      console.debug({
        microTasks: zone.getTasksFor('microTask'),
        macroTasks: zone.getTasksFor('macroTask'),
        eventTasks: zone.getTasksFor('eventTask')
      });

      // Advice how to find the origin of Zone tasks:
      console.debug(
        👀 For every pending Zone Task listed above investigate the stacktrace in the property 'creationLocation' 👆
      );
    }, delay);
  });
}

不要忘记在你的main.ts文件中导入zone.js/dist/task-tracking,也不要忘记在控制台中启用调试输出。祝你好运!
由于zone.js不再导出task-tracking,因此该路径无法访问,但在不导入该模块的情况下也可以正常工作。

inkz8wg9

inkz8wg98#

如果你的应用不稳定,这意味着有一些任务正在运行或等待执行。有一个更容易的方法来找出问题所在。我也遇到了同样的问题,并找到了一个技巧来捕获每个正在运行的宏任务。
将以下代码放入你的main.ts文件中。

import { AppModule } from './app/app.module';
import { NgZone } from '@angular/core';
import "../node_modules/zone.js/fesm2015/task-tracking";
import * as _ from "lodash";

platformBrowserDynamic()
  .bootstrapModule(AppModule)
  .then(moduleInstance => {
    // Ensure Angular destroys itself on hot reloads.
    if (window["ngRef"]) {
      window["ngRef"].destroy();
    }
    window["ngRef"] = moduleInstance;

const ngZone = moduleInstance.injector.get(NgZone);
setInterval(() => {
    var taskTrackingZone = (<any>ngZone)._inner.getZoneWith("TaskTrackingZone");
    if (!taskTrackingZone) {
        throw new Error("'TaskTrackingZone' zone not found! Have you loaded 'node_modules/zone.js/dist/task-tracking.js'?");
    }
    var tasks: any[] = taskTrackingZone._properties.TaskTrackingZone.getTasksFor("macroTask");
    tasks = _.clone(tasks);
    if (_.size(tasks) > 0) {
        console.log("ZONE pending tasks=", tasks);
    }
}, 1000);

    // Otherwise, log the boot error
  })
  .catch(err => console.error(err));

注意:导入“../node_modules/zone.js/fesm2015/task-tracking”;只需检查node_module文件夹内的文件路径。在你的情况下可能不同。我只是在fesm2015中找到了这个

如果代码中使用了setTimeout或setInterval,请使用以下代码:

import { NgZone, ChangeDetectorRef } from '@angular/core';

constructor(private ngZone: NgZone, private changeDetectorRef: ChangeDetectorRef) { }
    
    this.ngZone.runOutsideAngular(() => {
      this.subscription = interval(1000).subscribe(x => {
         console.log("Run ervery 1 seconds")
      });
    });

如果你使用了runOutsideAngular,Angular将无法检测到DOM或变量的变化。要检测Angular中的更改,请使用changeDetectorRef.detectChanges()

this.changeDetectorRef.detectChanges();

注意:NgZone是专门为Angular服务的API,允许在Angular区域内外运行代码。它允许开发人员控制代码运行的上下文,并提供钩子,以便在异步操作完成时通知开发者。

相关问题