Bir veri grid'i hydrate olmadan önce ne çizer
Bir kurulum rehberi ssr false'u zorunlu bir adım yapıyorsa, bileşenin sunucuda neye dokunduğunu sormaya değer. Bir grid'in orada render olabilmesi için gerçekte ne olması gerektiği, ve hydration sonrası ne değiştiği burada.
KanuniLabs5 dk okumaconst Grid = dynamic(() => import('grid-package'), { ssr: false }), kendiniz
seçtiğinizde makul bir satır. Kendi anasayfamız canlı demoları için bunu
kullanıyor; demolar sayfanın ilk bundle'ını büyütmek yerine ayrı bir chunk
olarak yükleniyor. Bir kurulum rehberi onu zorunlu bir adım yaptığında ise
durum farklı, çünkü o zaman anlamı şu: bileşen sunucuda hiç çalışamıyor,
bu yüzden sayfa bir yükleme placeholder'ı gönderiyor, JavaScript geldikten
sonra gerçek grid'i yerine koyuyor, ve bundle'ınızı çalıştırmayan her
crawler grid'in olması gereken yerde boş bir kutu görüyor.
"Bileşen çok fazla etkileşimli state tutuyor" bunu zorunlu kılmıyor.
Zorunlu kılan genellikle bileşenin kodunun bir effect'in dışında dokunduğu
belirli bir şey — window, document, ResizeObserver, ölçülmüş bir
eleman boyutu — ve bunların hiçbiri Next sayfanızı bir string'e render
ederken sunucuda yok, çünkü o sunucu hiçbir zaman bir tarayıcı sekmesi
açmadı.
Sunucuda gerçekte ne kırılıyor
Bir grid üç tür iş yapar, ve bunlardan yalnızca biri gerçekten bir sunucu sorunu.
Veriyi ve sütunları okumak sıradan JavaScript'tir. Bir diziyi dolaşmak
veya bir columns prop'unu okumak için tarayıcıya ihtiyaç yoktur.
Arayüzün şeklini çizmek — başlık, araç çubuğu, ARIA rolleri, sütun başlıkları — sıradan React'tir. Diğer her bileşen gibi bir string'e render olur.
Görüntü alanını ölçmek ve kaç satırın sığdığına karar vermek henüz
mümkün değildir. Tarayıcı henüz hiçbir şeyi yerleştirmedi. useEffect
içinde değil de render yolunda ResizeObserver çağıran veya
element.clientHeight okuyan bir grid, Node'da hata fırlatır — ve ssr: false, o hatanın nedenini bulmadan en hızlı yok olma yoludur.
Grid'imiz her iki tarafta ne çiziyor
| Sunucuda | Hydration sonrası | |
|---|---|---|
| Kök, başlık, ARIA rolleri | render edilir | değişmez |
| Sütun başlıkları | render edilir | değişmez |
| Satır penceresi | boş | ölçülür ve doldurulur |
| Web Worker | hiç oluşturulmaz | ~20.000 satır eşiğinin üstünde oluşturulur |
Uzak DataSource | load() bir kez çağrılır, hiç beklenmez | istemci yükler |
Satır penceresi boş, çünkü satır sayısı konteynerin yüksekliğine bağlı ve sunucunun ölçebileceği bir konteyneri yok. Tahmin edebilirdi — yaygın bir görüntü alanı yüksekliği diye, mesela, 20 satır render edebilirdi — ama bir tahmin çoğu ekran için yanlış olur, ve istemci gerçek konteyneri ölçtüğü an o satırları değiştirirdi. Sunucunun gerçekten gönderdiği şey iskelet ve sütun başlıkları, yani hydration öncesi sayfa boş bir div değil, okunabilir gerçek bir grid şekli.
O tablonun bir satırı iki kez okunmaya değer. Uzak bir kaynakta sunucu
render'ı load()'unuzu bir kez çağırıyor ve onu beklemiyor; markup
satırsız gidiyor, istemci de hydration sonrası kendi satırlarını yüklüyor.
Mutlak bir URL ile bu, her sayfa render'ında cevabı atılan fazladan bir
istek demek. Göreli bir fetch('/api/...') ise sunucuda sadece başarısız
oluyor, ve grid bu hatayı render'ı bozmadan yutuyor.
Bu, güvenmenizi istediğimiz bir iddia değil — DOM'suz bir ortamda bileşeni bir string'e render eden testlerle kapsanıyor: biri araç çubuğu, filtre satırı, başlık filtresi, grup paneli, sayfalayıcı ve özet çubuğu aynı anda açıkken, ayrı bir tanesi de uzak bir veri kaynağının, sunucu onu beklemeden render edildiğini kontrol ediyor.
Next.js'te bu neye benziyor
Grid bir client component, çünkü state tutuyor ve olaylara abone oluyor — bu kısım gerçekten kaçınılmaz, bu veya herhangi bir etkileşimli bileşende. Kaçınılabilir olan şey, bu sınırı tüm sayfaya kadar yukarı itmek.
'use client';
import { DataGrid } from '@kanunilabs/datagrid-react';
import '@kanunilabs/datagrid-react/styles.css';
export function SalesGrid({ rows }) {
return <DataGrid gridId="sales" dataSource={rows} rowKey="id" columns={columns} />;
}
Bir server component satırları çeker ve bir prop olarak aşağı geçirir;
yalnızca <DataGrid>'i render eden yaprak 'use client''a ihtiyaç duyar.
Stylesheet import'u aynı dosyada veya kök layout'ta yaşayabilir; ikisinden
hangisini tercih ediyorsanız orada bir kez import edin.
Worker'ın çözülecek bir dosyaya ihtiyacı yok
Bir bileşenin SSR'dan vazgeçmesinin bir başka nedeni de bir Web Worker olabilir: bundler'lar worker URL'lerini build zamanında çözer, ve bu çözümlemeyi sunucuda yanlış yapmak kendi başına bir hata sınıfı. Worker'ı paketin içine derliyoruz ve çalışma zamanında bir Blob URL'inden başlatıyoruz, yani Next'in — ya da Vite'ın, ya da başka herhangi bir şeyin — bir sunucu render'ı sırasında çözmesi gereken bir worker dosyası yok. Yapılandıracak hiçbir şey yok, çünkü bulunacak hiçbir şey yok.
Yine de iki durum gerçek dosyayı istiyor, ve o iki durum için dosya paketin
içinde gönderiliyor: worker-src … blob: içermeyen bir Content-Security-
Policy, ve worker'ı bundle'ınıza gömülü değil, kendi başına önbelleklenen
bir dosya olarak istemek. Her iki durumda da onu kendi origin'inizden
sunun — tarayıcı başka bir alan adından gelen worker betiğini başlatmaz.
cp node_modules/@kanunilabs/datagrid-core/dist/datagrid.worker.js public/
<DataGrid worker={{ url: '/datagrid.worker.js' }} /* ... */ />
Worker herhangi bir nedenle başlayamazsa — kimsenin güncellemeyi hatırlamadığı sıkı bir CSP, Blob URL'lerinin kullanılamadığı bir ortam — grid bomboş kalmaz. Aynı sıralama ve filtreleme hattını ana iş parçacığında çalıştırır ve yalnızca development'ta, bir kez uyarı basar. Kullanıcı hiçbir şey fark etmez, tek fark çok büyük bir sıralamanın artık ana iş parçacığından zaman çalması, ondan taşınmak yerine.
Bir grid size ssr: false dayatıyorsa neyi kontrol etmeli
- Bir effect'in dışında
window,documentveya birResizeObserver'a dokunuyor mu? Gerçek kısıt bu, ve "tüm bileşen sunucu tarafında render edilemiyor"dan daha dar. - Bundler'ınızın build zamanında çözmesi gereken bir worker dosyasına ihtiyacı var mı? Bu, vazgeçmek için ikinci, bağımsız bir neden, ve ya worker'ı derlenmiş halde göndererek ya da bundler'a onun için açık, statik bir import vererek çözülebilir.
- Hydration öncesi markup gerçekte ne içeriyor? Boş bir placeholder ve gerçek başlıklarla render edilmiş bir iskelet, bir crawler için farklı sayfalardır — oturum açmış bir kullanıcı ikisinde de aynı son grid'i görse bile.
JavaScript kapalıyken deneyin, ya da sayfaya curl atın — başlık satırı ve
sütun başlıkları zaten orada olmalı. DataGrid sayfamız gerçek
bir grid'i sunucuda render ediyor, yani
curl -s https://kanunilabs.com/datagrid | grep -o 'role="columnheader"' | wc -l
sütun başlıklarını doğrudan HTML'den sayıyor. Başlıklar orada değilse, SSR
desteği yanıt gövdesinde kontrol edebileceğiniz bir şey değil.
Satır penceresi, konteyneri ölçerek hydration sonrası dolar — aynı görüntü alanı matematiği sanal kaydırma yazısında anlatılıyor. Next.js, Vite ve diğer bundler'lar için tam kurulum SSR & bundler'lar sayfasında.