Skip to content

Angular 23 Zoneless 変更検出の深度解析

Angular 23 の Zoneless 変更検出は、Angular アーキテクチャにおける大きな革新だ。Zone.js への依存を排除し、変更検出をより予測可能で効率的にした。

Zone.js の歴史的負債

Zone.js は Angular 2 から変更検出のコアだった:

javascript
// Zone.js の動作原理
Zone.current.fork({
  onInvokeTask: (delegate, zone, target, task, ...args) => {
    const result = delegate.invokeTask(target, task, ...args);
    ngZone.runOutsideAngular(() => {
      ngZone.run(() => {});
    });
    return result;
  }
});

Zone.js の問題:

  1. パフォーマンスオーバーヘッド:非同期操作ごとに変更検出が発生する可能性
  2. デバッグ困難:エラースタックが Zone.js でラップされる
  3. 予測不可能:いつ変更検出が発生するかわからない
  4. バンドルサイズ膨張:Zone.js 自体が約 15KB

Zoneless の動作原理

Zoneless モードでは、変更検出は開発者によって明示的に制御される:

typescript
@Component({
  selector: 'app-counter',
  template: `
    <div>{{ count }}</div>
    <button (click)="increment()">+1</button>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class CounterComponent {
  count = 0;
  
  increment() {
    this.count++;
    this.cdr.markForCheck();
  }
  
  constructor(private cdr: ChangeDetectorRef) {}
}

または Signal を使用:

typescript
@Component({
  selector: 'app-counter',
  template: `
    <div>{{ count() }}</div>
    <button (click)="increment()">+1</button>
  `
})
export class CounterComponent {
  count = signal(0);
  
  increment() {
    this.count.update(n => n + 1);
    // Signal は自動的に変更検出をトリガー
  }
}

Zone.js からの移行

移行は段階的に行う:

ステップ 1:Zoneless モードを有効化

typescript
// main.ts
bootstrapApplication(AppComponent, {
  providers: [
    provideZonelessChangeDetection()
  ]
});

ステップ 2:変更検出を修正

typescript
// 之前:Zone.js に依存
ngOnInit() {
  this.userService.getUsers().subscribe(users => {
    this.users = users;
  });
}

// 之后:明示的にマーク
ngOnInit() {
  this.userService.getUsers().subscribe(users => {
    this.users = users;
    this.cdr.markForCheck();
  });
}

ステップ 3:Signal を使用(推奨)

typescript
@Component({...})
export class UserListComponent {
  users = signal<User[]>([]);
  
  ngOnInit() {
    this.userService.getUsers().subscribe(users => {
      this.users.set(users);
    });
  }
}

Zoneless の利点

利点 1:パフォーマンス向上

ベンチマーク結果:

  • 初期レンダリング:20-30% 向上
  • インタラクション応答:15-25% 向上
  • メモリ使用量:10-20% 削減

利点 2:デバッグがより明確に

// Zone.js モード
Error at ZoneDelegate.invokeTask (zone.js:402)
  at Zone.runTask (zone.js:175)
  at Component.handleClick (component.ts:15)  // 実際のエラー位置

// Zoneless モード
Error at Component.handleClick (component.ts:15)  // 直接定位

利点 3:より予測可能に

Zoneless モードでは、変更検出は以下の場合のみトリガーされる:

  • イベントハンドラ実行後
  • markForCheck() 呼び出し後
  • Signal 値変化後
  • detectChanges() 明示的呼び出し後

Signal と Zoneless の連携

Signal は Zoneless モードの最良のパートナー:

typescript
@Component({
  selector: 'app-product-list',
  template: `
    <div *ngFor="let product of filteredProducts()">
      {{ product.name }} - {{ product.price }}
    </div>
  `
})
export class ProductListComponent {
  products = signal<Product[]>([]);
  searchTerm = signal('');
  
  filteredProducts = computed(() => {
    const term = this.searchTerm();
    return this.products().filter(p => 
      p.name.toLowerCase().includes(term.toLowerCase())
    );
  });
  
  constructor() {
    effect(() => {
      console.log('商品数:', this.filteredProducts().length);
    });
  }
}

まとめ

Angular 23 の Zoneless 変更検出は、アプリケーションをより効率的で予測可能にする。移行は段階的に行い、手動マークより Signal を優先的に使用すべきだ。2026 年の Angular 開発において、Zoneless はオプションではなく推奨されるベストプラクティスである。

MIT Licensed