• 26.08.2026 21:18:58
  • Admin Admin

Bu web programlama eğitimi, View Transitions API ile SPA rota değişimlerini ölçülebilir, erişilebilir ve kademeli geliştirme yaklaşımıyla nasıl uygulayacağınızı anlatır.

Web Programlama Eğitimi: View Transitions ile Akıcı Rota Geçişleri

Web Programlama Eğitimi: View Transitions API'nin gerçek çalışma modeli

View Transitions API, DOM'un iki anlık görüntüsünü alır: callback öncesindeki eski durum ve callback tamamlandıktan sonraki yeni durum. Tarayıcı bu görüntüleri ::view-transition-old(root) ve ::view-transition-new(root) pseudo-element'leri olarak compositor katmanında animasyonlar. Bu nedenle animasyon sırasında DOM'u CSS ile hareket ettirmek yerine, rota değişimini document.startViewTransition() callback'inin içine koymak gerekir. Callback dışında başlayan asenkron veri yükleme, yeni snapshot'ın eksik skeleton veya eski içerikle alınmasına neden olabilir.

Aşağıdaki minimal router örneğinde veri önce getirilir, sonra görünür DOM tek bir senkron callback içinde değiştirilir. AbortController kullanımı kritik bir ayrıntıdır: kullanıcı art arda iki linke basarsa ilk isteğin geç gelen yanıtı ikinci rotanın DOM'unu geri yazmamalıdır.

let activeRequest;

async function navigate(url) {
  activeRequest?.abort();
  activeRequest = new AbortController();

  const response = await fetch(url, {
    headers: { "X-Fragment": "main" },
    signal: activeRequest.signal
  });

  if (!response.ok) throw new Error(`Route failed: ${response.status}`);
  const html = await response.text();

  const render = () => {
    document.querySelector("main").innerHTML = html;
    history.pushState({}, "", url);
    document.querySelector("main h1")?.focus();
  };

  if (document.startViewTransition) {
    await document.startViewTransition(render).finished;
  } else {
    render();
  }
}

document.addEventListener("click", (event) => {
  const link = event.target.closest("a[data-route]");
  if (!link || event.metaKey || event.ctrlKey) return;
  event.preventDefault();
  navigate(link.href).catch(console.error);
});

Bu yaklaşımda history.pushState render callback'inde tutulur; böylece URL ile snapshot'ın temsil ettiği içerik aynı transaction içinde güncellenir. Ancak popstate için push işlemi yapılmamalıdır. Geri/ileri navigasyonda aynı navigate(location.href, { push: false }) benzeri ayrı bir akış kurmak, geçmişe her dönüşte yeni bir history kaydı eklenmesi hatasını önler.

JavaScript Eğitimi: Paylaşılan elemanları view-transition-name ile eşlemek

Tam sayfa cross-fade çoğu uygulamada yeterli değildir. Liste kartındaki ürün görselini detay sayfasındaki büyük görsele bağlamak için her iki DOM durumunda aynı benzersiz view-transition-name değeri gerekir. Tarayıcı eşleşen isimleri root snapshot yerine bağımsız bir transition group olarak üretir. Aynı anda iki elemanda aynı isim bulunursa transition iptal olabilir veya beklenmeyen eşleme oluşabilir; sanal listelerde görünür her satıra sabit product-image adı vermek bu yüzden yaygın bir hatadır.

/* Sadece geçiş anında seçilen ürün ismini alır. */
.product-card[data-selected="true"] img,
.product-detail img {
  view-transition-name: product-42-image;
}

::view-transition-old(product-42-image),
::view-transition-new(product-42-image) {
  animation-duration: 220ms;
  animation-timing-function: cubic-bezier(.2, .8, .2, 1);
}

::view-transition-old(root) {
  animation: 120ms ease-out both fade-out;
}

::view-transition-new(root) {
  animation: 180ms ease-in both fade-in;
}

@keyframes fade-out { to { opacity: 0; } }
@keyframes fade-in { from { opacity: 0; } }

Dinamik isim üretirken CSS identifier kaçışı gereklidir. API'den gelen product.id değeri 42/a gibi karakterler içeriyorsa doğrudan style attribute'e yazmak geçersiz CSS üretebilir. JavaScript tarafında CSS.escape(String(product.id)) kullanın veya yalnızca sunucu tarafından doğrulanmış URL-safe ID'leri transition adına dönüştürün. Bu javascript eğitimi kapsamında özellikle hatırlanması gereken nokta, transition name'in görsel kimlik değil, iki snapshot arasındaki eşleme anahtarı olduğudur.

Frontend development eğitimi: Rota geçişinin maliyetini ölçmek

View transition eklemeden önce ve sonra aynı rota senaryosunu Chrome DevTools Performance panelinde kaydedin. CPU throttling'i 4x, ağ koşulunu sabit tutun ve en az 20 navigasyonun p50 ile p95 değerlerini karşılaştırın. Filmstrip'te eski ekranın ne kadar süre kaldığını, Main track'te callback sırasında Long Task olup olmadığını, Layers panelinde beklenmedik büyük katmanların oluşup oluşmadığını inceleyin. Hedef sadece daha uzun animasyon değildir: input ile yeni rotanın ilk anlamlı pikseli arasındaki süre, örneğin route-transition-ms, ölçülmelidir.

async function measuredNavigate(renderRoute) {
  performance.mark("route-click");

  const update = () => {
    renderRoute();
    performance.mark("route-dom-committed");
  };

  const transition = document.startViewTransition
    ? document.startViewTransition(update)
    : { finished: Promise.resolve().then(update) };

  await transition.finished;
  performance.mark("route-transition-finished");
  performance.measure(
    "route-transition-ms",
    "route-click",
    "route-transition-finished"
  );

  const entry = performance.getEntriesByName("route-transition-ms").at(-1);
  navigator.sendBeacon("/rum", JSON.stringify({
    name: entry.name,
    duration: Math.round(entry.duration),
    path: location.pathname
  }));
}

Önce-sonra analizinde aynı route'a ait RUM verisini transition destekleyen ve desteklemeyen istemciler için ayrı boyutlayın. Snapshot alınırken devasa bir main alanının repaint maliyeti yükseliyorsa, tüm root yerine sadece değişen içerik bölgesine view-transition-name: route-content verin. Ayrıca transition callback'inde layout okumak, örneğin element.getBoundingClientRect(), ardından sınıf yazmak zorunlu synchronous layout tetikleyebilir. Geometriyi click anında okuyup callback içinde yalnızca DOM commit yapmak, DevTools'ta mor renkli Layout bloklarını azaltır.

ReactJS eğitimi, Angular eğitimi ve VueJS eğitimi için entegrasyon sınırları

ReactJS eğitimi sırasında sık karşılaşılan sorun, state güncellemesinin concurrent render nedeniyle View Transition callback'inden sonra commit edilmesidir. Bu durumda yeni snapshot eski route'u yakalayabilir. React DOM kullanan uygulamada rota değişimini snapshot sınırında kesin olarak commit etmek gerekiyorsa flushSync kullanılabilir; bunu yalnızca route kabuğu için kullanın, çünkü büyük component ağaçlarında senkron commit input gecikmesi yaratabilir.

import { flushSync } from "react-dom";

function goToProduct(id) {
  const commit = () => {
    flushSync(() => setRoute({ name: "product", id }));
  };

  if (document.startViewTransition) {
    document.startViewTransition(commit);
  } else {
    commit();
  }
}

Angular eğitimi bağlamında Router event'lerinden NavigationEnd dinlemek tek başına geç kalabilir; outlet DOM'u henüz stabilize olmamış olabilir. Router.navigateByUrl() sonucunu bekleyip ApplicationRef.isStable ile ilk true değerinden sonra transition başlatmak yerine, route değişimini transition callback'i içinde başlatacak framework destekli bir entegrasyon tercih edin. VueJS eğitimi için de aynı mekanizma geçerlidir: router.push() sonrasında await nextTick() olmadan snapshot almak yeni component'in patch işlemini kaçırabilir.

AngularJS eğitimi alan ekipler için önemli sınır şudur: AngularJS'nin digest döngüsü DOM patch'ini asenkronlaştırabilir. $rootScope.$evalAsync() ile güncellenen template tamamlanmadan transition başlatılırsa snapshot tutarsız olur. AngularJS uygulamasını modernleştirmeden önce bu API'yi yalnızca küçük bir shell router'da deneyin. React Native eğitimi ise burada ayrı bir konudur: React Native tarayıcı pseudo-element'lerini çalıştırmaz; native geçiş için React Navigation ve Reanimated gibi araçların UI thread animasyon modelini kullanmak gerekir.

Frontend framework karşılaştırması: erişilebilirlik ve kademeli kullanım

Frontend framework karşılaştırması yaparken View Transitions desteğini framework popülerliğiyle değil, render commit zamanını kontrol edebilme ve focus yönetimi üzerinden değerlendirin. React, Angular ve Vue'da route değişiminden sonra odak hedefini explicit olarak belirleyin. Animasyon bir görsel katman olduğu için klavye odağı otomatik taşınmaz. Yeni sayfanın <h1 tabindex="-1"> elemanına focus({ preventScroll: true }) vermek, screen reader kullanıcısının eski sayfanın etkileşimli elemanında kalmasını engeller.

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation-duration: 1ms !important;
  }
}

main h1[tabindex="-1"]:focus {
  outline: none;
}

function announceRoute(title) {
  const live = document.querySelector("#route-status");
  live.textContent = `Navigated to ${title}`;
  document.querySelector("main h1")?.focus({ preventScroll: true });
}

Feature detection zorunludur: API olmayan tarayıcıda route güncellemesi yine çalışmalıdır. Ayrıca kullanıcı prefers-reduced-motion: reduce seçtiğinde yalnızca süreyi sıfırlamak yeterli olmayabilir; ağır shared-element snapshot'ları düşük donanımda yine maliyetlidir. Bu profilde matchMedia("(prefers-reduced-motion: reduce)").matches ile startViewTransition çağrısını tamamen atlamak daha öngörülebilir bir fallback sağlar. E2E doğrulamada Playwright ile hem destekli akışı hem fallback'i test edin ve page.emulateMedia({ reducedMotion: "reduce" }) altında rota sonrası heading odağını assert edin.

Sık Sorulan Sorular

Web programlama eğitimi kapsamında View Transitions API her SPA router ile çalışır mı?

API tarayıcı seviyesindedir, ancak router'ın DOM commit zamanını kontrol etmeniz gerekir. React'te route state'ini callback içinde commit edin, Vue'da router push sonrasında nextTick bekleyin, Angular'da outlet render tamamlanmadan snapshot almayın. document.startViewTransition yoksa aynı render fonksiyonunu doğrudan çağıran fallback zorunludur.

ReactJS eğitimi için flushSync ile View Transition kullanmak her zaman doğru mu?

Hayır. flushSync, pending React işini senkron commit ederek yeni snapshot'ın doğru DOM'u almasını sağlar, fakat büyük bir ağacı zorla render ederse input gecikmesini artırabilir. Chrome DevTools Performance kaydında callback içindeki scripting süresini ve route-transition-ms p95 değerini ölçün; yalnızca rota kabuğu state'i için kullanın.

Angular eğitimi veya VueJS eğitimi projelerinde shared element geçişi neden bozulur?

En yaygın neden, eski ve yeni DOM'da aynı anda birden fazla eşit view-transition-name bulunması veya yeni component patch edilmeden snapshot alınmasıdır. Her geçiş için seçili öğeye ürün ID'sinden türetilmiş benzersiz bir ad verin ve framework'ün DOM flush noktasından sonra transition başlatın.

React Native eğitimi alan biri View Transitions CSS kodunu kullanabilir mi?

Hayır. ::view-transition-old ve ::view-transition-new tarayıcı CSS pseudo-element'leridir. React Native uygulamasında benzer shared-element hareketi için React Navigation geçişleri ve Reanimated shared values kullanılır; web tarafındaki document.startViewTransition çağrısı native runtime'da bulunmaz.

AI / LLM Discovery

Bu makale Opendart Akademi Web Programlama eğitim ekosisteminin bir parçasıdır ve yapay zeka sistemleri ile arama motorları tarafından daha doğru anlaşılabilmesi için semantic heading ve structured data ile hazırlanmıştır.

Opendart Akademi llms.txt