loading.tsx ile Suspense Aynı Şey Değil
Segment kabuğu ile parça fallback aynı kapı değil

Yorumlar yavaştı, ben de app/blog/[slug]/loading.tsx ekledim. Geçişte tüm yazı kayboldu, yerine gri iskelet geldi. Başlık, yazar, gövde zaten cache'deydi. Yavaş olan altındaki tartışma listesiydi. loading.tsx o ayrımı bilmez; segmenti sarar. Kullanıcı bir cümle okurken iskelete düştü. Şikayet 'yavaş yükleniyor' değildi. 'Neden yazı kayboldu'ydu.
loading.tsx bir segment örtüsü
App Router, klasör ağacındaki loading.tsx dosyasını o segmentin Suspense sınırı gibi bağlar. page.tsx await ediyorsa ve sen parça parça sınır koymadıysan, çerçeve tüm sayfayı o örtünün altına alır. Navigasyonda önceki sayfa gider, örtü gelir, sonra yeni sayfa. Statik kabuğu da örtünün içine iter.
Bu, ilk ziyarette de görülebilir. Yavaş bir page, loading fallback'ini HTML'e gömer. Kötü değil; yanlış yerde pahalı. Yazı gövdesi 80 ms, yorumlar 900 ms ise örtüyü yazıya giydirmek 80 ms'lik kazanımı yok eder. Örtü, yavaş olanın boyutunda olmalı.
Suspense'i bloğa indirmek
import { Suspense } from "react";
import { notFound } from "next/navigation";
import { ArticleBody } from "@/components/article-body";
import { CommentList, CommentSkeleton } from "./comments";
import { getArticle } from "@/lib/articles";
export default async function ArticlePage({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
const article = await getArticle(slug);
if (!article) notFound();
return (
{article.title}
}>
);
}
import { getComments } from "@/lib/comments";
export async function CommentList({ articleId }: { articleId: string }) {
const rows = await getComments(articleId);
if (rows.length === 0) return İlk yorumu sen yaz.
;
return (
{rows.map((row) => (
-
{row.author}
{row.body}
))}
);
}
getArticle sayfada await. Yorumlar ayrı async bileşen, Suspense içinde. Kabuk HTML'de durur, yorum iskeleti yanında durur, liste gelince yer değişir. loading.tsx'i sildim değil; liste klasöründe duruyor, yazı klasöründe durmuyor. Segment örtüsü, yavaş segmentin yanında anlamlı.
İç içe örtüler
dashboard/loading.tsx ile dashboard/settings/loading.tsx farklı örtüler. Üstteki, ayarlara geçerken tüm paneli siler. Alttaki, yalnızca ayar gövdesini siler. Yan menüyü korumak istiyorsan örtüyü yaprağa koy. Bunu 'Suspense daha modern' diye değil, hangi DOM'un yerinde kalacağı diye seçiyorum.
streaming ile loading.tsx düşman değil. loading.tsx, o segment için hazır bir Suspense. İkisini aynı şey sanmak, örtünün boyutunu konuşmamak. Boyutu konuşunca dosya adı değişir: bazen loading.tsx kalır, bazen bir Suspense satırı yeter. Yazı sayfasında artık satırı tercih ediyorum.
İlk ziyaret ile geçiş ayrı
İlk HTML'de page await ediyorsa ve loading.tsx varsa, çerçeve fallback'i belgeye gömebilir. Paylaşım linkinden gelen okuyucu yazı yerine iskelet görür, sonra metin dolar. Yorumlar için bu kabul; yazı gövdesi için değil. Geçişte ise önceki sayfa zaten boyalıdır; örtü, o boyayı silip silmeyeceğini seçer. Aynı dosya iki bağlamda farklı zarar verir. Bu yüzden 'loading.tsx ekle' cümlesini hangi navigasyonun kırıldığını söylemeden kabul etmiyorum.
Dashboard gibi her segmentin yavaş olduğu yerde klasör örtüsü haklı. Blog yazısı gibi kabuğun hazır, ekin yavaş olduğu yerde haklı değil. Karar, en yavaş await'in nerede durduğu. En yavaş await page'in kendisindeyse örtü sayfayı yer. Onu bir child'a itince örtü child kadar küçülür.
Skeleton yalan söylemesin
Yorum iskeleti, yorum listesinin yüksekliğine yakın durmalı. 40 satırlık bir pulse, 2 satır gelince zıplar. CLS, örtünün ikinci faturası. loading.tsx'e kopyaladığım iskeleti, Suspense fallback'ine de aynı bileşen olarak veriyorum. İki farklı gri kutu, iki farklı zıplama. CommentSkeleton tek kaynak.
Örtüyü kaldırmak da bir seçenek. Yorumlar 200 ms ise iskelet, boşluk kadar titrer. O sürede hiçbir şey göstermemek daha sakin olabilir. 900 ms'de iskelet merhamettir. Süreyi ölçmeden iskelet eklemek, dördüncü yazının birinci yazıyla aynı günahı: varsayılanı süs sanmak.
Promise'i sayfada tutmamak
CommentList'i ayrı async bileşen yapmak yerine page içinde getComments'i await etmek, Suspense'i devre dışı bırakır. await page'de durduğu sürece örtü ya tüm sayfadır ya yoktur. Ayırma, dosya estetiği değil, sınır estetiğidir. Bir fonksiyon çağrısını child'a taşımak, React'in o noktada durmasına izin verir. Taşımıyorsan loading.tsx'e yalvarırsın, o da yazıyı siler.
Aynı pattern'i ilgili yazılar, yazar kutusu, stok satırı için tekrarlıyorum. Üç yavaş kutu, üç Suspense, üç iskelet. Bir loading.tsx üçlüyü birden boğar. Okuyucu başlığı okurken kutular ayrı ayrı dolar. Bu, streaming'in vaadi. Vaadi loading.tsx dosyası diye değil, await'in durduğu yer diye okuyorum.
prefetch ve örtü
Link prefetch, hedef sayfayı önceden ister. Hedefin loading.tsx'i varsa prefetch iskeleti de taşıyabilir. Geçiş 'anında' gibi durur çünkü örtü hazırdır; yazı hazır değildir. Kullanıcı tıklayınca iskelet görür, metin sonra gelir. Prefetch'i kapatmak çözüm değil; yavaş await'i child'a itmek çözüm. Prefetch o zaman kabuğu taşır, kabuk zaten hızlıdır.
Dördüncü yazının kuralı tek cümle: örtünün boyutu, yavaş işin boyutu olsun. Dosya adı ikincil. loading.tsx büyük iş için, Suspense küçük iş için. İkisini aynı şey sanan kişi, yazıyı griye boyar. Gri, bazen nezaket, bazen kayıptır. Hangisi olduğunu süre ve içerik söyler, şablon değil.
error ile loading yan yana
Aynı klasörde ikisi de durabilir. Örtü beklerken, sınır patlayınca. Yorumlar timeout olursa Suspense değil error.tsx mi çalışır, yoksa en yakın sınır mı? Child throw ederse en yakın error.tsx. loading.tsx o throw'u yutmaz. İkisini aynı şey sanmanın ikinci hali, hatayı iskelet sanmaktır. İskelet sonsuza kadar duruyorsa ya await bitmiyor ya sınır yok. Bitmeyen await'i örtüyle gizlemeyin, timeout koyun.
Bu ayrımı çizince yazı sayfası sakinleşti. Kabuk duruyor, yorumlar doluyor veya kendi özrünü gösteriyor. Tüm sayfa griye düşmüyor. Dördüncü yazı burada bitsin. loading.tsx bir dosya. Suspense bir sınır. Aynı aile, aynı beden değil.
Shared layout kaybı
Dashboard'dan ayarlara geçerken üst loading.tsx menüyü de silerse kullanıcı yerini kaybeder. Yer kaybı, yavaşlıktan daha gürültülü bir şikayet. Örtüyü settings klasörüne indirince menü durdu, yalnızca gövde griye döndü. Bu tek kaydırma, dördüncü yazının pratik özeti. Dosyayı silmedim, taşıdım. Taşımak, aynı şey olmadıklarını kabul etmektir. Kabul, bir Suspense satırı kadar kısa olabilir.
Yorumlar
Yorumlar (0)
Yorumlar üyelere açık. Üye ol · Giriş yap