Нормально ли для Angular повторный рендеринг бесконечно, даже если не было внесено никаких изменений?

Вопрос

Разработан ли Angular для постоянной перепроверки всего, чтобы обнаружить изменения? Я исхожу из мира React, и я ожидал что-то вроде event triggered -> re-rendering, Я не знаю, что-то из моего приложения или из Angular.

Если у меня есть метод, который вызывается из шаблона HTML, он вызывается бесконечно, даже если в моем компоненте ничего не меняется.

Эта проблема

У меня есть страница, похожая на календарь, которая загружает много данных и должна что-то вычислять перед рендерингом, потому что было бы слишком сложно сделать это с ngIf директивы. Так что в моем шаблоне есть вещи, которые выглядят так:

<div [innerHTML]="_getDayPrice(item, day) | safeHtml"></div>  

Если я утешаю лог что-то в _getDayPrice метод, он печатается бесконечно.

Что я пробовал

  1. Мне удалось обойти это, вручную вводя ChangeDetectionRef в моем приложении и делаю this.cdRef.detach(), Это, однако, кажется хакерским, так как иногда мне может понадобиться включить его и отключить снова.

  2. Я пытался выяснить, является ли это чем-то из моих родительских компонентных приложений, таких как контейнеры. Я сделал в моем главном app.component один div, как <div class={{computeClass()}}> и в этом методе печатается консольный журнал и, конечно же, он вызывается бесконечно. После этого я попытался закомментировать все сервисы приложений. Если все закомментированы достаточно точно, это работает правильно, но также нет никаких наблюдаемых данных. Я исследовал около половины дня и не смог найти ни единой точки отказа (например, комментирование этого сервиса исправляет все).

  3. Запишите производительность, используя встроенную вкладку "Производительность" Chrome, но опять же не смог найти ничего из моего кода, который вызывает изменения. zone.js вызывается несколько раз и, кажется, устанавливает интервал, который продолжает срабатывать.

  4. Конечно, я искал случаи setTimeout а также setInterval в службах, но не смог найти что-то, что постоянно меняется, что может вызвать эту проблему.

Заключение?

Итог: нормально ли это, если у вас есть сложное приложение Angular и вызывается метод из шаблона, чтобы этот метод вызывался бесконечно?

А если нет, есть ли у вас какие-либо намеки на то, что может быть причиной этого? Или каким-либо другим способом обойти это, а не отключать детектор changeRef?

Мое единственное беспокойство связано с производительностью. Это похожая на календарь страница, отображающая несколько строк, и она довольно сильно отстает на ноутбуке с 8 ГБ ОЗУ. Я почти уверен, что планшет или телефон почти замерзнут.

2 ответа

Решение

Вы должны использовать каналы как можно чаще при преобразовании данных в HTML.

Каналы переоцениваются только при изменении объекта или параметров конвейера (поэтому, если один из входных данных является объектом, обязательно создайте новый экземпляр этого объекта для запуска переоценки). В большинстве случаев вы можете использовать канал, а не функцию.

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'getDayPrice'
})
export class GetDayPricePipe implements PipeTransform {

  transform(item: string, day: string): string {
    // ... do whatever logic here
    return item + day;
  }

}

Тогда используйте это так:

<div [innerHTML]="item | getDayPrice:day | safeHtml"></div>

Существует два доступных механизма для уменьшения количества проверок привязок

1 - ChangeDetectorRef.detach: Разрешить отсоединение вашего компонента от цикла обнаружения изменений, поэтому ставки пока не будут обновляться до повторного присоединения.

2 - ChangeDetectionStategy.OnPush: сообщите Angular, что привязки вашего компонента необходимо проверять только тогда, когда хотя бы один из компонентов @Input изменился. Вы можете найти мне подробную информацию о том, как использовать его здесь

IMO, вы должны создать компонент с входами item / days, обертывающими ваш элемент DOM дисплея. Что-то вроде

// wrap.component.ts
@Component({
  template: '<div [innerHTML]="_getDayPrice() | safeHtml"></div>',
  changeDetection: ChangeDetectionStrategy.OnPush,
  ...
})
export class WrapComponent {
  @Input() item: Item;
  @Input() day: String; 

  private _getDayPrice = () => {
      // compute your HTML with this.item and this.day instead of parameters
      ....
  }
}

Тогда вы должны увидеть логику, срабатывающую только при изменении предмета или дня.

Другие вопросы по тегам