您如何确定是什么触发了 Angular2 中的变化检测检测(调试 ngDoCheck 无限循环)?

How can you identify what's triggering round of change detection detection in Angular2 (debugging ngDoCheck infinite loop)?

我无法进行最小复制,我可以放入 plunkr,但在我的应用程序中,我有一个无限循环,其中根组件中的 ngDoCheck 被无限调用,所以我会希望能够识别 实际发生的变化。

我在 ngDoCheck 中放置断点后查看了堆栈跟踪中的每个调用,但找不到任何有用的东西。

我知道从 变化检测中触发变化检测会导致这种无限循环(尽管我认为我启用的调试模式会捕获它)但是我无法手动找到任何实例。

是否有任何可以记录或检查的内容可以深入了解导致一轮更改检测的原因?


可能不是很有用,但是在这个循环中有堆栈和状态的快照:

可能有点晚了,但对某些人来说还是有帮助的。

我找到了几个解决方案,可以帮助您找到真正导致 CD 循环的原因。

第一个,正如评论中提到的,检查任务的source字段,最简单的方法是

 // put this in any component which is rendered on the page
  public ngDoCheck() {
    console.log('doCheck', Zone.currentTask.source);
  }

这将输出如下内容:

这已经是东西了,你有进一步研究的方向,但还不够完善。

要找到定义动作处理程序的真实位置,我们需要区域的 long-stack-trace 规范。

// add this, for example, in your polyfills file 
import 'zone.js/dist/long-stack-trace-zone';

现在除了任务源之外,我们还可以检查 long-stack-trace 准备的数据

  public ngDoCheck() {
    console.log('doCheck', Zone.currentTask.source, Zone.currentTask.data.__creationTrace_);
  }

这将输出一个数组:

我们对数组中每个条目的 error 属性 感兴趣。 从 0 元素开始在控制台中调查,然后更进一步。

在这个 属性 中会有这样的堆栈跟踪:

也许这种方式过于复杂,但这是我通过阅读 Zone 的源代码发现的。 如果有人提出更简单的解决方案,我们将非常高兴。

更新 1

我创建了一个小片段,可以帮助您提取长堆栈跟踪

function renderLongStackTrace(): string {
  const frames = (Zone.currentTask.data as any).__creationTrace__;
  const NEWLINE = '\n';

  // edit this array if you want to ignore or unignore something
  const FILTER_REGEXP: RegExp[] = [
    /checkAndUpdateView/,
    /callViewAction/,
    /execEmbeddedViewsAction/,
    /execComponentViewsAction/,
    /callWithDebugContext/,
    /debugCheckDirectivesFn/,
    /Zone/,
    /checkAndUpdateNode/,
    /debugCheckAndUpdateNode/,
    /onScheduleTask/,
    /onInvoke/,
    /updateDirectives/,
    /@angular/,
    /Observable\._trySubscribe/,
    /Observable.subscribe/,
    /SafeSubscriber/,
    /Subscriber.js.Subscriber/,
    /checkAndUpdateDirectiveInline/,
    /drainMicroTaskQueue/,
    /getStacktraceWithUncaughtError/,
    /LongStackTrace/,
    /Observable._zoneSubscribe/,
  ];

  if (!frames) {
    return 'no frames';
  }

  const filterFrames = (stack: string) => {
    return stack
      .split(NEWLINE)
      .filter((frame) => !FILTER_REGEXP.some((reg) => reg.test(frame)))
      .join(NEWLINE);
  };

  return frames
    .filter(frame => frame.error.stack)
    .map((frame) => filterFrames(frame.error.stack))
    .join(NEWLINE);
}

用法:

  public ngDoCheck() {
    console.log(renderLongStackTrace());
  }

更新 2

我发现对于 EventTask,zone 的 long-stack-trace 创建了错误的堆栈跟踪,您可以在此处跟踪此问题 https://github.com/angular/zone.js/issues/1195

更新 3

在应用程序 bootstrap 之前设置 Error.stackTraceLimit = Infinity; 是值得的(不适用于生产),因为带有 angular + 区域的堆栈跟踪可能真的很长。