Что произойдет, если передать на вход @Input объект при включенной стратегии OnPush
На одном из собеседований мне задали вопрос — есть компонент FooComponent
, с включенной стратегией обнаружения изменения OnPush
и в него передаётся объект. Что происходит в этот момент под капотом? Создает ли Angular новый объект, вынуждая FooComponent пройти проверку? Попробуем разобраться.
@Component({ template: ` <app-foo [config]="{ position: 'top' }"></app-foo> ` }) export class AppComponent { }
@Component({ selector: 'app-foo', changeDetection: ChangeDetectionStrategy.OnPush }) export class FooComponent { @Input() config: Config; }
В нашем случае, когда Angular компилирует шаблон, он замечает, что объект статический и не использует каких-либо привязок. Поэтому Angular
создает его один раз, кэширует и всегда возвращает ту же ссылку при последующих проверках.
Если мы посмотрим на скомпилированный код, мы увидим, что Angular
использует функцию pureFunction0
, которая делает то, что мы описали выше:
const _c0 = function() { return { position: "top" }; }; export class AppComponent {} AppComponent.ɵcmp = ɵɵdefineComponent({ type: AppComponent, template: function AppComponent_Template(rf, ctx) { if (rf & 2) { ɵɵproperty("config", ɵɵpureFunction0(1, _c0)); } }, directives: [i1.FooComponent] });
Перейдем к следующему примеру, где значение одного из свойств объекта не является статическим:
@Component({ template: ` <app-foo [config]="{ position: position }"></app-foo> ` }) export class AppComponent { position = 'top'; change() { this.position = 'bottom'; } }
В этом случае для Angular не важен сам объект. Когда компилятор обнаруживает привязку, он сохраняет ее значение в структуре данных LView
. В коде имеется одна привязка, поэтому Angular
будет использовать версию pureFunction1
:
const _c0 = function() { return { position: "top" }; }; export class AppComponent {} AppComponent.ɵcmp = ɵɵdefineComponent({ type: AppComponent, template: function AppComponent_Template(rf, ctx) { if (rf & 2) { ɵɵproperty("config", ɵɵpureFunction1(1, _c0, ctx.position)); } }, directives: [i1.FooComponent] });
Эта функция вернет кэшированную ссылку на объект, если текущее значение position
такое же, как и кэшированное. В противном случае она вернет новый объект, содержащий обновленное значение свойства.
Результирующий объект будет передан функции ɵɵproperty
, которая использует функцию Object.is
для проверки необходимости обновления свойства.
В заключение, единственным недостатком при использовании таких объектов вместо свойства компонента является то, что компилятор будет гсоздавать и выполнять дополнительный код.