Framework entegrasyonları · Vue + Three
Mount & Kök Sözleşmesi: Sahne Nerede Doğar?
Vue ürününde WebGL sahnesi çoğu zaman bir
mount noktasının tuvalinde açılır. Bu sayfa
createApp / onMounted / provide ile kök
sözleşmeyi anlatır: Vue 101 veya Composition API turu değil · Three ↔ mount kapısı.
Holodepth bu yaprağı «Vue nedir?» dersi veya R3F
<Canvas> kopyası olarak yazmaz. R3F kök mührü
Canvas & kök sözleşme
yaprağındadır; Angular kardeş köprüsü
Host & kök sözleşmesi
yaprağındadır. Mercek burada mount + ref + provide/inject ömrü.
Kare metronom
Kare döngüsü & watchEffect
yaprağındadır. Dispose ve loader borusu
Yükleme & onBeforeUnmount
yaprağındadır. glTF şema format izleklerindedir.
Vue + Three yolculuğunun omurgası şu zincirde okunur: mount → harita → tik → kargo → bağ → kaçış. Bu sayfa mount kapısıdır: sahne nerede doğar, renderer kime aittir, tek WebGL bağlamı nasıl mühürlenir? Sonraki yapraklar şablon grafiği, kare döngüsü, yükleme ömrü, olay bağları ve reaktif kaçışı derinleştirir.
Sayfayı bitirdiğinizde şu cümleyi kurabilmelisiniz: «Mount bileşeni gümrük masasıdır. Renderer onMounted'ta açılır, onBeforeUnmount'ta kapanır. Template ref CSS kutusu ile backing store'u ayırır. provide/inject kökü alt ağaca taşır. Tek canlı context varsayılandır. SSR'de ClientOnly kapısı zorunludur. Sonraki kapı reaktif şablon haritasıdır.»
Bu sayfanın sınırı · komşu konularla ayrım
Bu sayfa Vue + Three'de mount kök sözleşmesini anlatır:
createApp/ mount noktası sahipliğionMountedaçılış · template ref · boyut / DPRprovide/injectile renderer + sahne kökü- Tek context · SSR / ClientOnly kapısı
- Üç mount profili (ham Three · TresJS tarzı · Nuxt)
Bilinçli olarak dışarıda bırakılan konular:
- R3F
Canvas/ onCreated → R3F · Canvas & kök - Angular
AfterViewInit/ ElementRef → Angular · Host & kök - Şablon ↔ Object3D → Reaktif şablon → Object3D
- watchEffect / rAF → Kare döngüsü & watchEffect
- Loader / onBeforeUnmount dispose derinliği → Yükleme & onBeforeUnmount
- Reaktif kaçış / performans → Reaktif kaçış & performans
- Vue temeli / SFC 101 · FPS ms SLA → Platform · glTF/GLB → format izlekleri
Kısa ayrım: Bu sayfa «Vue mount kapıyı nasıl mühürler?» · R3F Canvas «Fiber kapıyı nasıl mühürler?» · Angular host «bileşen tuvali kapıyı nasıl mühürler?» · Şablon haritası «kapının arkasında reaktif ağaç».
Gümrük masası: mount noktası
Imperatif demoda çoğu ekip index.html içine
<canvas> koyup script'te renderer açar. Vue ürününde tuval bir
SFC şablonuna aittir: stil, boyut, yaşam döngüsü ve provide hattı bu masadan geçer.
Holodepth: mesh'i yanlış yazmak sahneyi bozar; mount'u yanlış mühürlemek tüm oturumu bozar.
createApp(App).mount('#app') çağrısı DOM ağacını Vue'ya bağlar; Three
kökü ise tipik olarak ayrı bir Stage/Scene bileşeninin içinde doğar. TresJS
tarzı <TresCanvas> bu masayı soyutlar; ham Three kullanıyorsanız
sözleşme aynıdır: kim canvas DOM'unu tutar, kim WebGLRenderer'ı yaratır, kim yok eder?
Mount masası üç soruya cevap verir: (1) WebGL hangi DOM düğümünde? (2) Renderer hangi bileşen ömrüne yazılı? (3) Alt bileşenler köke nasıl erişir? Bu üçlü mühürlenmeden reaktif şablon veya picking yapraklarına geçmek erken adımdır.
Dekoratif şablon taşımaz; mount bileşeni sözleşmeyi taşır.
onMounted: kapı açılışı
setup() veya <script setup> içinde template ref henüz
DOM'a bağlı değildir. Holodepth kök kuralı: renderer ve ilk
setSize tipik olarak onMounted (veya eşdeğeri «DOM hazır»
geri çağrısı) sonrasındadır. Erken erişim «null ref» veya 0×0 tampon üretir.
Kapanış: rota değişince bileşen ölür; GL kaynakları
onBeforeUnmount / onUnmounted hattına yazılır. Derin dispose,
texture ve loader sahipliği
Yükleme & onBeforeUnmount
yaprağındadır; burada mühür: açılış ve kapanış aynı mount ömrüne bağlıdır.
nextTick bazen layout settle için gereklidir: özellikle flex/grid içinde
ilk clientWidth sıfır dönebilir. Holodepth: onMounted içinde bir kez
ölç, ResizeObserver ile sürdür; her reaktif güncellemede renderer yaratma.
Template ref: iki defter bağlantısı
CSS kutusu (layout) ile backing store (piksel tamponu) yine ayrıdır. Mount şablonunda
width/height: 100% ve min-height yoksa R3F'deki «siyah kutu»
veya Angular'daki «sıfır yükseklik» tuzağını Vue'da da yaşarsınız. Holodepth:
ref="canvasHost" ile tuvali alın; clientWidth/clientHeight ve
devicePixelRatio tavanıyla setSize /
setPixelRatio yazın.
| Defter | Vue yüzü | Kök kararı |
|---|---|---|
| Layout | Host CSS / flex / min-height | Tuval görünür alan |
| Backing store | setSize + DPR tavanı |
Fill-rate bütçesi |
| Yaşam | onMounted ↔ onBeforeUnmount | Context ömrü |
| Kök erişim | provide / inject |
Alt SFC'lerin sahne sözleşmesi |
Container ref ile canvas ref ayrımı: bazı ekipler renderer'ı doğrudan
<div ref="host"> üzerinde appendChild(canvas) ile kurar;
bazıları şablonda <canvas ref="gl"> kullanır. İkisi de geçerli;
karar ürün stiline bağlıdır. Önemli olan ref'in onMounted'ta dolu olması ve parent'ın
ölçülebilir bir kutusu olmasıdır.
provide/inject: kök taşıyıcı
Mount bileşeni renderer ve THREE.Scene (veya TresJS eşdeğeri) yarattıktan
sonra bunları provide(THREE_ROOT_KEY, { renderer, scene, camera }) ile
alt ağaca sunar. Child mesh bileşenleri inject ile köke erişir; her child'ın
kendi WebGLRenderer'ı olmaz. Bu, Angular'daki root-provided servis veya R3F context'e
benzer diplomatik hat: tek kök, çok dal.
Injection key sembol veya string olabilir; Holodepth önerisi: çakışmayı önlemek için
Symbol('three-root') veya namespaced string. Provide yalnızca mount ağacında
bir kez yapılır; nested ikinci provide ikinci kök yaratır ve «hangi scene?» tartışması
başlatır.
TresJS tarzı kütüphaneler bu provide katmanını içeride çözer; ham Three'de siz yazarsınız. Kütüphane seçimi mount sözleşmesini değiştirmez: yine tek context, yine mount ömrü.
SSR ve ClientOnly kapısı
Nuxt / Vite SSR ortamında window, document ve WebGL yoktur.
Holodepth: kök açılışı <ClientOnly> (Nuxt) veya
import.meta.env.SSR / typeof window !== 'undefined'
kapısı arkasındadır. Sunucuda renderer yaratmaya çalışmak build'i değil, hydration'ı kırar.
Hydration sonrası ilk frame: mount host boyutunun settle olmasını bekleyin; aksi 1×1 tampon riski. Bu Nuxt hydration dersi değil; Three mount zamanlamasıdır. SSR HTML'de canvas placeholder bırakılabilir; gerçek WebGL yalnızca istemcide açılır.
Dinamik import (defineAsyncComponent) ile Stage bileşenini istemciye
erteleyen ekipler de aynı kapıyı farklı kapı koluyla geçer. Önemli olan: sunucu
render yolunda Three modülünün yan etkisiz kalması.
Renderer mührü: GL imzası
Antialias, alpha, powerPreference, outputColorSpace, toneMapping ürün sabitleri mount
açılışında bir kez mühürlenir. Her child'da rastgele değiştirmek «neden renk kaydı?»
tartışması üretir. R3F'de bu gl / onCreated yüzüdür; Angular'da host
factory; Vue'da aynı diplomatik karar mount bileşeni (veya provide edilen factory)
üzerindedir.
Kare metronom ve watchEffect politikası
Kare döngüsü & watchEffect
yaprağındadır. Burada yalnız: renderer varoluş sözleşmesi mount ömrüne yazılır;
requestAnimationFrame döngüsü mount'ta başlatılabilir ama reaktif bağ
kuralları sonraki yaprakta.
Güvenli mount: simüle mühür
Örnekler öğretim amaçlı, güncel Vue 3 + Three yüzeyine yaslanan simüle kalıplardır. Pinia / Vuex veya build aracı seçimi öğretilmez. Paket sürümlerinizi doğrulayın.
Simüle · vitrin mount'u. Template ref; açılış onMounted; provide kök; kapanış onBeforeUnmount iskeleti.
<script setup>
import { onBeforeUnmount, onMounted, provide, ref, shallowRef } from 'vue'
import * as THREE from 'three'
import { THREE_ROOT_KEY } from './threeRootKey'
const hostRef = ref(null)
const renderer = shallowRef(null)
const scene = shallowRef(null)
const camera = shallowRef(null)
let resizeObs = null
onMounted(() => {
// SSR: bu blok yalnızca istemcide çalışmalı (ClientOnly veya env kapısı).
const host = hostRef.value
if (!host) return
const canvas = document.createElement('canvas')
canvas.style.display = 'block'
canvas.style.width = '100%'
canvas.style.height = '100%'
host.appendChild(canvas)
const r = new THREE.WebGLRenderer({
canvas,
antialias: true,
alpha: false,
powerPreference: 'high-performance',
preserveDrawingBuffer: false,
})
r.outputColorSpace = THREE.SRGBColorSpace
r.toneMapping = THREE.ACESFilmicToneMapping
const s = new THREE.Scene()
const c = new THREE.PerspectiveCamera(50, 1, 0.1, 100)
c.position.set(0, 0, 4)
renderer.value = r
scene.value = s
camera.value = c
provide(THREE_ROOT_KEY, { renderer: r, scene: s, camera: c })
const applySize = () => {
const w = host.clientWidth
const h = host.clientHeight
if (w < 2 || h < 2) return
const dpr = Math.min(window.devicePixelRatio || 1, 2)
r.setPixelRatio(dpr)
r.setSize(w, h, false)
c.aspect = w / h
c.updateProjectionMatrix()
}
applySize()
resizeObs = new ResizeObserver(() => applySize())
resizeObs.observe(host)
})
onBeforeUnmount(() => {
resizeObs?.disconnect()
const r = renderer.value
r?.dispose()
renderer.value = null
scene.value = null
camera.value = null
// Geometri / texture dispose borusu Yükleme & onBeforeUnmount yaprağında derinleşir.
})
</script>
<template>
<div
ref="hostRef"
class="stage-root"
style="width:100%;height:100%;min-height:360px;position:relative"
/>
</template>
Simüle · kök anahtarı ve sahiplik notu. inject tarafı child SFC'lerde kullanılır; birden fazla StageRoot varsa ikinci renderer'ı varsayılan yapmayın.
import { inject } from 'vue'
/** @typedef {{ renderer: import('three').WebGLRenderer, scene: import('three').Scene, camera: import('three').Camera }} ThreeRoot */
export const THREE_ROOT_KEY = Symbol('holodepth-three-root')
/** @type {'mount-local' | 'app-shared'} */
export const RENDERER_OWNERSHIP = 'mount-local'
export function useThreeRoot() {
const root = inject(THREE_ROOT_KEY, null)
if (!root) {
throw new Error('[Holodepth] THREE_ROOT_KEY bulunamadı · StageRoot altında mısınız?')
}
return /** @type {ThreeRoot} */ (root)
}
export function assertSingleContext(liveRendererCount) {
if (RENDERER_OWNERSHIP === 'mount-local' && liveRendererCount > 1) {
console.warn('[Holodepth] Birden fazla canlı context · mobil bütçeyi kontrol edin.')
}
}
Güvenlik: kullanıcıdan gelen canvas boyutlarını sınırsız şişirmeyin; DPR tavanı zorunlu.
SSR'de native DOM API çağrılarını ClientOnly veya env kapısı olmadan çalıştırmayın.
shallowRef ile renderer/scene'yi derin reaktif yapmayın; ağır nesneler
proxy tuzağına düşer.
Büyütme, DPR ve ResizeObserver
Pencere veya yan panel açılınca mount host ölçüsü değişir. Holodepth varsayılanı:
ResizeObserver ile setSize yolunu tetiklemek; ham
window.resize dinleyicisi yalnızca bilinçli tercih. Her
watch veya watchEffect turunda setSize çağırmak thrashing
üretir; seyreltin.
devicePixelRatio tavanı (tipik 2) fill-rate bütçesini korur. Retina'da
keskinlik isterken mobilde ısınma riski taşırsınız. DPR kararı mount mührünün parçasıdır;
child bileşenlerde rastgele değiştirmeyin.
setSize(w, h, false) üçüncü argüman CSS boyutunu canvas stiline yansıtmaz;
layout zaten host CSS'inden gelir. İki defter kuralı: CSS %100, backing store DPR ile çarpılır.
İki bağlam tuzağı
Her mount kartında yeni WebGLRenderer = yeni context. Dashboard'da üç küçük
Vue bileşeni üç GPU oturumu açabilir. Holodepth varsayılanı: mümkün olduğunca tek
canlı renderer; paylaşım gerekiyorsa provide kökü bilinçli tasarlanır, «her
widget kendi Three'si» alışkanlığı yapılmaz.
İkinci tuzaq: aynı sayfada hem ham StageRoot hem TresJS
<TresCanvas> denemesi. İki soyutlama katmanı iki context üretir.
Ticket'ta sahiplik cümlesi yazın: renderer mount'ta mı, app-level composable'da mı?
Üçüncü tuzaq: keep-alive ile cache'lenen mount bileşeninde observer ve
renderer'ı yeniden bağlamayı unutmak. Aktivasyon/deaktivasyon kancalarında ResizeObserver
ve render döngüsü politikası net olmalı; aksi «donmuş kare» veya «çift observer» üretir.
Üç mount profili
-
01 · Mount
Ürün vitrini (ham Three)
- Sahiplik
- mount-local renderer + provide
- DPR
- Tavan 2 · keskinlik
- Risk
- İkinci rota ile ikinci StageRoot
-
02 · Mount
TresJS tarzı canvas
- Sahiplik
- Kütüphane iç provide · tek context
- Boyut
- Sabit min-height · ResizeObserver
- Risk
- Ham Stage ile çift canvas
-
03 · Mount
Nuxt SSR + ClientOnly
- Kapı
- ClientOnly / dynamic import
- Açılış
- onMounted + layout settle
- Risk
- Sunucuda WebGL denemesi
Mount tuzakları
- Erken ref: setup içinde hostRef.value null.
- 0 yükseklik: flex child min-height yok.
- Derin reaktif Three: ref(renderer) proxy thrash.
- Çoklu context: kart başı renderer veya çift canvas.
- SSR çağrısı: ClientOnly kapısız WebGL.
- watch thrash: her state'te setSize.
- Unmount yok: rota sonrası context sızıntısı.
- R3F ezberi: onCreated veya frameloop prop'u aramak.
- Angular ezberi: AfterViewInit / ElementRef beklemek.
Holodepth perspektifleri
Ömür = context
Renderer mount ömrüne yazılır. Orphan context mobilin sessiz düşmanıdır. onBeforeUnmount'ta dispose silüeti burada; geometri ve texture derinliği kargo yaprağındadır.
Bir context varsayılan
İkinci mount ikinci gerekçe ister; widget ızgarası gerekçe değildir. provide kökü paylaşım için tasarlanır, çoğaltma için değil.
İki defteri ayırın
CSS kutusu ile DPR tamponu aynı watch içinde konuşulmamalı. Layout CSS'te,
backing store renderer'da kalır.
shallowRef disiplini
Scene, renderer ve mesh referansları derin reaktif olmamalı. Reaktif kaçış ihtiyacı Reaktif kaçış & performans yaprağında; mount'ta yalnız «ağır nesne proxy'lenmez» mührü.
Teşhis maddeleri
- Renderer onMounted'ta mı açılıyor?
- Host min-height / boyut sözleşmesi var mı?
- DPR tavanı yazılı mı?
- Canlı context sayısı gerekçeli mi?
- SSR ClientOnly kapısı var mı?
- ResizeObserver thrash yapıyor mu?
- onBeforeUnmount renderer'ı salıyor mu?
- provide/inject kökü tek mi?
- R3F, Angular host veya glTF şema bu sayfaya sızmış mı?
Holodepth içgörüsü
«Vue'da Three çalışmıyor» çoğu zaman mesh hatası değil; mount zamanlaması, sıfır kutu, shallowRef ihlali veya çoklu context sorunudur. Önce gümrük masasını okuyun; sonra harita → tik → kargo zincirine geçin.
Sonraki kapı: reaktif harita
Kapı mühürlendikten sonra Vue şablonu (veya TresJS yapı taşları) Object3D ağacına nasıl bağlanır? Sonraki sayfa Reaktif şablon → Object3D bu eşlemeyi anlatır: mount → harita adımı. watchEffect döngüsü, yükleme borusu, olay bağları ve reaktif kaçış sonraki yapraklardadır.
Zinciri hatırlayın: mount (burada) · harita (şablon grafiği) · tik (kare döngüsü) · kargo (yükleme ömrü) · bağ (picking) · kaçış (performans). Her halka ayrı yaprak; bu sayfa yalnızca ilk mührü taşır.