holodepth

Framework entegrasyonları · Angular + Three

Yükleme & OnDestroy: Ömürle Mühürlenen Kargo

Asenkron doku veya model, host ömründen uzun yaşarsa sızıntı ve yarış üretir. Bu sayfa Angular'da yükleme ile ngOnDestroy sahipliğini anlatır: Suspense dersi değil · Three varlık ↔ bileşen ömrü.

Holodepth bu yaprağı RxJS 101 veya R3F Suspense turunun kopyası olarak yazmaz. R3F asenkron köprü Yükleme & Suspense köprüsü yaprağındadır. Host ve şablon önceki Angular yapraklarındadır; kare Kare döngüsü & NgZone yaprağındadır. glTF alan spekü format izleklerinde; sıkıştırma Platform · Asset yaprağındadır. Dispose matematiği Three kaynak omurgasına linklenir.

Sayfayı bitirdiğinizde şu cümleyi kurabilmelisiniz: «Yük, bileşen ömrüne bağlıdır. Destroy önce biterse abonelik iptal edilir. OnDestroy registry geometri/materyal/doku/renderer salar. Paylaşılan cache ile yerel sahiplik ayrılır. URL değişince önceki yük iptal olur. Sonraki kapı pointer bağları & picking'tir.»

Bu sayfanın sınırı · komşu konularla ayrım

Bu sayfa Angular + Three'de yükleme / OnDestroy sahipliğini anlatır:

  • Ömür pasaportu · yarış (destroy < load)
  • Dispose registry · paylaşım vs yerel
  • İptal / switchMap · hata yüzeyi
  • Üç sahiplik profili

Bilinçli olarak dışarıda bırakılan konular:

Kısa ayrım: Bu sayfa «kargo ömürle nasıl mühürlenir?» · R3F yükleme «Suspense askı köprüsü» · NgZone «tik yaşarken» · Format «paket içinde ne var?».

Ömür pasaportu

Angular bileşeni doğar, view hazır olur, yok olur. Three Texture / BufferGeometry / Object3D ise GPU'da durur; Angular onları otomatik «çöp toplamaz». Holodepth: her asenkron kargonun pasaportu host veya parça bileşeninin ömrüne yazılır. Rota değişince pasaport düşer; kargo ya iptal edilir ya temiz salınır.

«Yükledim sahneye ekledim» yetmez. Ticket cümlesi: kim yükler, kim dispose eder, paylaşım var mı?

Ölü bileşenin canlı dokusu, sessiz bellek sızıntısıdır.

Yarış: destroy yükü geçer

Klasik hastalık: kullanıcı hızlı gezinir; Promise/Observable çözülünce ölü bileşene scene.add veya setState benzeri yazım yapılır. Holodepth: yük başlarken iptal jetonu alın (takeUntilDestroyed, DestroyRef, veya Subject complete). Çözüm geldiğinde jeton düşmüşse sahneye dokunmayın; gelen kaynağı hemen dispose edin veya hiç kabul etmeyin.

Suspense modeli bu yarışı React ağacında askıyla çözer; Angular'da çözüm çoğunlukla iptal + guard'dır.

Yük yolları: Loader ve boru

Praxis'te iki yol yan yana durur: Three loader'ları (TextureLoader, model loader…) ve Angular HttpClient + decode. Holodepth şema açmaz: hangi yol seçilirse seçilsin, sonuç Object3D/Texture ömrü OnDestroy'a bağlanır. Loader eklentileri (DRACO vb.) uygulama bootstrap'ında bir kez kurulur; her bileşende yeniden yaratılmaz (host sayfasındaki tek-kök ruhu).

URL değişince iptal

Input olarak asset URL'si değişince önceki indirme sürüyorsa switchMap (veya eşdeğeri) eski akışı iptal eder; aksi hâlde iki sonuç yarışır, yanlış doku kazanabilir. Holodepth: kimlik (URL + sürüm) net; rastgele query ile cache delmek bilinçli busting dışında yasaktır.

OnDestroy registry

Dağınık dispose() çağrıları unutulur. Holodepth pratikte bir kaynak listesi tutar: geometri, materyal(ler), doku(lar), opsiyonel RT / composer. ngOnDestroy listeyi gezip salar; scene'den remove eder. Renderer host'a aittir (kök sayfa); parça bileşen renderer'ı öldürmez.

Üç sahiplik · karıştırmayın
Model Kim dispose eder? Ne zaman?
Bileşen yerel Yükleyen component ngOnDestroy
Servis cache Uygulama / bilinçli purge Ömür boyu veya manuel
Misafir Çağıran Sözleşmeye göre

Ortak materyali ilk çocuğun destroy'unda dispose etmek ikinci çocuğu öldürür. Cache modelinde dispose etmeyin.

Hata yüzeyi

404, CORS, bozuk buffer sessiz siyah sahne üretebilir. Angular'da hata catchError / şablon bayrağı / kullanıcı mesajı ile yüzeye çıkar. React Error Boundary kopyası değildir; ürün standardı: reject yutulmaz. Kullanıcı URL'sini doğrulamadan loader'a vermeyin.

Decode'u rAF'e gömmeyin

NgZone yaprağı tiki ince tutmayı mühürledi. Fetch + parse + texture upload'u her kareye yaymak jank üretir. Yük askı / Promise yolunda bitsin; yerleşince demand veya sürekli loop bir kare boyasın. Platform FPS dilim teşhisine köprüdır.

Yükleme şeridi ile kare şeridi ayrı metronomdur.

Güvenli ömür: simüle kargo

Örnekler öğretim amaçlıdır. RxJS operatör varyantları tam müfredat değildir; sabit allowlist URL kullanın.

Simüle · TextureLoader + takeUntilDestroyed.

import {
  Component, DestroyRef, Input, OnChanges, OnDestroy, SimpleChanges, inject,
} from '@angular/core'
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'
import { Observable, from } from 'rxjs'
import * as THREE from 'three'

const PANEL_MAP = '/assets/demo/panel-albedo.jpg' // allowlist · kullanıcı string'i yok

function loadTexture$(url: string): Observable<THREE.Texture> {
  return from(
    new Promise<THREE.Texture>((resolve, reject) => {
      new THREE.TextureLoader().load(url, resolve, undefined, reject)
    }),
  )
}

@Component({
  selector: 'app-panel-texture',
  template: '',
})
export class PanelTextureComponent implements OnChanges, OnDestroy {
  @Input({ required: true }) parent!: THREE.Object3D

  private readonly destroyRef = inject(DestroyRef)
  private mesh?: THREE.Mesh
  private map?: THREE.Texture
  private readonly mat = new THREE.MeshStandardMaterial({ roughness: 0.55, metalness: 0.05 })

  ngOnChanges(changes: SimpleChanges): void {
    if (!this.parent || this.mesh) return
    // Eğitim: URL sabit. Dinamik URL'de switchMap + iptal kullanın.
    loadTexture$(PANEL_MAP)
      .pipe(takeUntilDestroyed(this.destroyRef))
      .subscribe({
        next: (tex) => {
          tex.colorSpace = THREE.SRGBColorSpace
          this.map = tex
          this.mat.map = tex
          this.mat.needsUpdate = true
          this.mesh = new THREE.Mesh(new THREE.PlaneGeometry(2, 1.25), this.mat)
          this.parent.add(this.mesh)
        },
        error: () => {
          // Üründe kullanıcıya görünür hata yüzeyi
        },
      })
  }

  ngOnDestroy(): void {
    if (this.mesh) {
      this.parent?.remove(this.mesh)
      this.mesh.geometry.dispose()
      this.mesh = undefined
    }
    this.mat.dispose()
    this.map?.dispose()
    this.map = undefined
  }
}

Simüle · sahiplik notlu registry.

/** Yerel sahiplik · cache paylaşımında track etmeyin */
export class DisposeRegistry {
  private readonly geos = []
  private readonly mats = []
  private readonly maps = []

  trackGeo(g) { this.geos.push(g); return g }
  trackMat(m) { this.mats.push(m); return m }
  trackMap(t) { this.maps.push(t); return t }

  disposeAll() {
    for (const g of this.geos) g.dispose?.()
    for (const m of this.mats) {
      if (Array.isArray(m)) m.forEach((x) => x.dispose?.())
      else m.dispose?.()
    }
    for (const t of this.maps) t.dispose?.()
    this.geos.length = 0
    this.mats.length = 0
    this.maps.length = 0
  }
}

/**
 * ngOnDestroy:
 *   this.scene.remove(this.root)
 *   this.registry.disposeAll()
 * Renderer'ı burada öldürmeyin · host'a aittir.
 */

Güvenlik: allowlist yolları; origin kontrolü; hata mesajında dahili yol sızdırmayın; bozuk dosyayı başarılı saymayın.

Yükleme / destroy tuzakları

  1. Guard yok: ölüye add.
  2. URL yarışı: switchMap unutuldu.
  3. Ortak mat dispose: komşu ölür.
  4. Renderer child destroy: host'u kırar.
  5. rAF içi decode: jank.
  6. Sessiz catch: siyah sahne.
  7. Suspense ezberi: Angular'da aynı API yok.

Üç sahiplik profili

  • 01 · Kargo

    Vitrin kahramanı

    Sahiplik
    Bileşen yerel registry
    İptal
    takeUntilDestroyed
    Risk
    Hızlı rota yarışı
  • 02 · Kargo

    Paylaşılan kütüphane

    Sahiplik
    Servis cache
    Dispose
    İlk unmount'ta kesme
    Risk
    Ömür boyu şişme
  • 03 · Kargo

    Dinamik URL paneli

    Akış
    switchMap + allowlist
    UI
    Hata yüzeyi zorunlu
    Risk
    Önceki dokunun geç gelmesi

Holodepth perspektifleri

Ömür = yetki

Destroy düşmüşse kargo sahneye yazılmaz.

Registry yazın

Dağınık dispose unutulur; liste unutulmaz.

Paylaşımı etiketleyin

Cache ise dispose etme; yerel ise et.

Teşhis maddeleri

  1. takeUntilDestroyed / iptal var mı?
  2. URL değişiminde switchMap var mı?
  3. Sahiplik modeli yazılı mı?
  4. Registry geometri/mat/doku kapsıyor mu?
  5. Renderer yanlışlıkla child'da mı ölüyor?
  6. Hata yüzeyi sessiz mi?
  7. Decode rAF içinde mi?
  8. Suspense veya glTF şema bu sayfaya sızmış mı?

Holodepth içgörüsü

«Model geldi sonra patladı» çoğu zaman format değil; yarış veya yanlış dispose'tur. Önce ömür pasaportunu okuyun.

Sonraki kapı: pointer bağları

Kargo yerleşince kullanıcı dokunur. Sonraki sayfa Pointer bağları & picking Angular olay bağları ile Three isabetini anlatır. Zone kaçışı son yapraktadır.