Change Detection
Nima bu?
Default change detection strategiyasi (ChangeDetectionStrategy.Default) har bir asinxron hodisa (click, HTTP, setTimeout, Zone.js event) dan keyin komponent daraxtining butun qismini tekshiradi. Har qanday o'zgarish topilsa template qayta render qilinadi. Sodda, lekin katta ilovalarda ortiqcha ishlash yukini keltirishi mumkin.
Kod misoli
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
selector: 'app-default-cd',
standalone: true,
// Default — ko'rsatmasa ham shu
changeDetection: ChangeDetectionStrategy.Default,
template: `
<p>Counter: {{ counter }}</p>
<p>User: {{ user.name }}</p>
<button type="button" (click)="increment()">+1</button>
<button type="button" (click)="mutateUser()">Mutate</button>
`,
})
export class DefaultCdComponent {
counter = 0;
user = { name: 'Ali' };
increment(): void {
this.counter++;
}
mutateUser(): void {
// Default CD: reference o'zgarmasa ham zone event CD ni ishga tushiradi
this.user.name = 'Vali';
}
}
Imtihonda
- Change detection qachon trigger bo'ladi?
- Default strategiya nima uchun "xavfsiz" hisoblanadi?
- Zone.js'siz (zoneless) default CD qanday ishlaydi?
Yodlash uchun
Default = har event'dan keyin butun tree tekshiriladi; Zone.js odatda trigger qiladi.
Nima bu?
ChangeDetectionStrategy.OnPush komponentni faqat quyidagi holatlarda tekshiradi: yangi @Input reference; komponent ichidagi event; async pipe yangi qiymat olganda; markForCheck() chaqirilganda. Immutable data pattern bilan performance sezilarli yaxshilanadi.
Nega async pipe OnPush bilan "sehrli" ishlaydi: AsyncPipe ichida subscribe() chaqiriladi, va har emit'da pipe o'zi ChangeDetectorRef.markForCheck() ni chaqiradi — bu template'dagi boshqa hech qanday RxJS subscribe (masalan komponent kodida qo'lda .subscribe()) qilmaydi. Shuning uchun OnPush komponentda ma'lumotni qo'lda subscribe qilib class field'ga yozsangiz, view yangilanmay qoladi — sabab input reference o'zgarmagani, async pipe esa yo'q. Bu — real interview'da eng ko'p uchraydigan OnPush "capqoni".
Kod misoli
import { Component, ChangeDetectionStrategy, Input } from '@angular/core';
import { AsyncPipe } from '@angular/common';
import { Observable, of } from 'rxjs';
interface Todo {
id: number;
title: string;
done: boolean;
}
@Component({
selector: 'app-todo-item',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<label>
<input type="checkbox" [checked]="todo.done" (change)="toggle()" />
{{ todo.title }}
</label>
`,
})
export class TodoItemComponent {
@Input({ required: true }) todo!: Todo;
@Input() onToggle!: (id: number) => void;
toggle(): void {
this.onToggle(this.todo.id);
}
}
@Component({
selector: 'app-todo-list',
standalone: true,
imports: [TodoItemComponent, AsyncPipe],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
@for (todo of todos$ | async; track todo.id) {
<app-todo-item [todo]="todo" [onToggle]="toggleTodo" />
}
`,
})
export class TodoListComponent {
todos$ = of<Todo[]>([
{ id: 1, title: 'Angular o\'rganish', done: false },
]);
toggleTodo = (id: number): void => {
// Immutable update — yangi array reference
};
}
Imtihonda
- OnPush'da ob'ekt property'sini mutate qilsa nima bo'ladi?
asyncpipe nima uchun OnPush bilan yaxshi juft?- Barcha komponentlarni OnPush qilish mumkinmi?
- OnPush komponentda service'dan qo'lda
.subscribe()qilib class field yangilansa, nega view yangilanmaydi?
Yodlash uchun
OnPush = input ref o'zgarishi, event, async pipe, markForCheck; mutate emas, replace.
Nima bu?
ChangeDetectorRef komponentning change detection tsiklini qo'lda boshqarish uchun inject qilinadi. markForCheck() — keyingi CD tsiklda tekshirish (OnPush uchun); detach() — CD'dan ajratish; detectChanges() — darhol shu komponent va child'larni tekshirish; reattach() — qayta ulash.
Kod misoli
import {
Component,
ChangeDetectionStrategy,
ChangeDetectorRef,
inject,
OnInit,
OnDestroy,
} from '@angular/core';
@Component({
selector: 'app-live-price',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
template: `<p>Narx: {{ price }} so'm</p>`,
})
export class LivePriceComponent implements OnInit, OnDestroy {
price = 0;
private cdr = inject(ChangeDetectorRef);
private timerId?: ReturnType<typeof setInterval>;
ngOnInit(): void {
// setInterval Zone tashqarisida — markForCheck kerak
this.timerId = setInterval(() => {
this.price += Math.floor(Math.random() * 1000);
this.cdr.markForCheck();
}, 2000);
}
forceUpdate(): void {
this.cdr.detectChanges(); // darhol tekshir
}
ngOnDestroy(): void {
clearInterval(this.timerId);
}
}
Imtihonda
markForCheck()vsdetectChanges()farqi?detach()qachon foydali (virtual scroll)?- OnPush + WebSocket/stream'da qanday yangilash?
Yodlash uchun
markForCheck — keyingi tsikl; detectChanges — hozir; OnPush + tashqi event = markForCheck.
Nima bu?
Zoneless Angular zone.js kutubxonasiz change detection ishlaydi. Hodisalar va signallar o'zgarishi orqali CD aniq trigger qilinadi — bundle kichikroq, debug osonroq, performance bashorat qilinadigan. Angular 18+ da provideExperimentalZonelessChangeDetection() bilan yoqiladi.
Zone.js aslida nima qiladi: u brauzer API'larini (setTimeout, addEventListener, Promise, XHR) monkey-patch qiladi va har birining tugashidan keyin Angular'ga "signal" beradi — shu signal butun daraxt bo'ylab CD'ni ishga tushiradi. Bu — nega ba'zan kutilmagan joyda (masalan uchinchi tomon kutubxona ichidagi event) ham CD ishga tushishi yoki, aksincha, runOutsideAngular() bilan ataylab zone'dan tashqariga chiqarilgan kod CD trigger qilmasligi mumkinligining sababi. Zoneless'da bunday "yashirin" trigger yo'q — har bir signal yangilanishi aniq va bashorat qilinadigan, lekin zone tashqarisidagi eski (signal ishlatmaydigan) kod endi CD'ni avtomatik trigger qilmaydi — shuning uchun migratsiya paytida signal'ga o'tmagan komponentlar "muzlab qolishi" mumkin.
Kod misoli
// main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import {
provideExperimentalZonelessChangeDetection,
} from '@angular/core';
import { AppComponent } from './app/app.component';
bootstrapApplication(AppComponent, {
providers: [
provideExperimentalZonelessChangeDetection(),
],
});
import { Component, signal, computed } from '@angular/core';
@Component({
selector: 'app-zoneless-counter',
standalone: true,
template: `
<p>{{ count() }}</p>
<p>Kvadrat: {{ squared() }}</p>
<button type="button" (click)="increment()">+1</button>
`,
})
export class ZonelessCounterComponent {
count = signal(0);
squared = computed(() => this.count() ** 2);
increment(): void {
this.count.update(v => v + 1);
// Zoneless: signal o'zgarishi CD ni avtomatik trigger qiladi
}
}
Imtihonda
- Zone.js nima qiladi va nega olib tashlanmoqda?
- Zoneless'da
setTimeoutichidagi o'zgarish qanday ko'rinadi? - Migratsiya strategiyasi qanday?
NgZone.runOutsideAngular()nima uchun ishlatiladi va zoneless'da bu tushuncha qanday o'zgaradi?
Yodlash uchun
Zoneless = signal + explicit CD; zone.js ixtiyoriy; v18+ experimental provider.
