Sunucu Taraflı Oluşturma (Server-Side Rendering - SSR)
PreffX, bileşenlerinizi sunucu tarafında bir HTML dizgisine (string) dönüştürür ve ardından aynı ağacı istemci tarafında "hydrate" eder (canlandırır). Sunucu, işaretleme (markup) koduna ek olarak, çözümlenmiş asenkron verileri içeren bir "ön yükleme" (preload) betiği gönderir; istemci bu betiği okur ve verileri yeniden getirmek (fetch etmek) yerine doğrudan ağaca dahil eder.
SSR yardımcı araçları preffx/server modülünden içe aktarılır.
Sunucu tarafında createRoot
Sunucu, istemci tarafındakiyle aynı işlevselliğe sahip ancak mount yerine renderToString metodunu döndüren kendine özgü bir createRoot dışa aktarır.
import { createRoot } from 'preffx/server';
import { App } from './App';
const server = createRoot();
const { preload, serialize } = server.renderToString(App, {
props: { name: 'PreffX' }
});
await preload(); // resolve every async resource
const html = serialize(); // markup + preload data scriptGenellikle her istek için yeni bir kök (root) oluşturulur; çünkü uygulama, istek URL'sine (yönlendirme, dil) veya istek kapsamındaki bağlama (context) bağlı olabilir:
import { createRoot } from 'preffx/server';
export async function render(url: URL) {
const root = createRoot({
defaultURL: url,
defaultLang: 'en',
context: { tenant: 'acme' }
});
const { preload, serialize } = root.renderToString(App, {});
await preload();
return { appHtml: serialize() };
}API
createRoot(config?)
İstemci köküyle (client root) aynı kök seçeneklerini kabul eder; böylece yapılandırma senkronize kalır:
| Seçenek | Tür | Açıklama |
|---|---|---|
defaultURL | string | URL | Yönlendirme (routing) için başlangıç URL'si |
defaultLang | string | Uluslararasılaştırma (i18n) için başlangıç dili |
prefix | string | Kök bazlı önek (istemci köküyle eşleşmelidir) |
context | object | Ağaç yapısıyla paylaşılan, istek kapsamlı bağlam |
Tek bir metot içeren bir nesne döndürür:
renderToString(component, config?)
component— kök bileşen (PCveyaAPC).config— isteğe bağlı yapılandırma; şunları içerir:props— kök bileşen için özellikler (props).serializer— önceden yüklenmiş (preloaded) verileri serileştirmek için kullanılan özel bir işlev (varsayılan değer:JSON.stringify).
Dönen değer: { preload, serialize }
| Metot | İmza | Açıklama |
|---|---|---|
preload | preload(timeout?) | Kayıtlı tüm asenkron kaynakları çözümler (resolve eder). İsteğe bağlı bir zaman aşımı (timeout) değeri, yavaş bir kaynağın "elinden gelenin en iyisi" (best-effort) prensibiyle işlenmesini sağlar; bu durumda sayfa yine de oluşturulur ve istemci (client) veriyi daha sonra tekrar çeker. |
serialize | serialize(): string | HTML dizisini (işaretleme/markup + kök başına önceden yüklenmiş veri betiği) oluşturur ve döndürür. |
Sunucudaki kaynaklar
Bir bileşen içinde resource, sunucuda şeffaf bir şekilde davranır: bir "effect" çalıştırmak yerine, veri getirme işlevini (fetcher) preload() işlemi için kaydeder. Aynı bileşeni hem sunucu hem de istemci tarafında yeniden kullanabilirsiniz; herhangi bir koşullu ayrım (branch) yapmanıza gerek kalmaz.
import type { PC } from 'preffx';
export const UserCard: PC = (_props, { resource, computed }) => {
const [user] = resource(async () => {
const res = await fetch('/api/user');
return res.json();
});
const display = computed(() => user.state.value?.name ?? 'loading');
return (
<article className="user">
<h1>{display}</h1>
</article>
);
};preload()çalışmadan önceuser.state.valuedeğerinull,pendingisetruedurumundadır; bu nedenle yedek içerik ('loading') görüntülenir.preload()tamamlandıktan sonra, elde edilen değer işaretleme (markup) içine yerleştirilir; ayrıcaserialize()fonksiyonu da bu değeri ön yükleme (preload) veri betiğine yazar.- İstemci tarafında "hydration" (hidrasyon) aşamasında kaynak, önceden yüklenmiş olan değeri okur ve veri getirme (fetch) işlemini atlar; böylece
pendingdeğerifalseolarak kalır.
İstemci tarafında "hydration"
İstemci tarafındaki createRoot().mount() çağrısı, sunucu tarafından oluşturulan ön yükleme veri betiğini algılar, bunu ağaca dahil eder ve mevcut DOM yapısını yeniden oluşturmak yerine onu "hydrate" eder (canlandırır/etkinleştirir) — böylece fazladan bir çağrıya gerek kalmaz.
import { createRoot } from 'preffx';
import { App } from './App';
createRoot().mount(App, {
node: document.getElementById('app')!
});Başarılı bir hydration (hidrasyon) süreci için gereklilikler:
- İstemci tarafındaki
createRootseçenekleri (prefix,defaultLang,defaultURL,context), sunucu tarafındakilerle eşleşmelidir. - Oluşturulan işaretleme (markup), tam olarak istemcinin beklediği yere (
mountile referans verilen kapsayıcıya) yerleştirilmelidir.
Örnek: Tam istek akışı
// entry-server.tsx
import { createRoot } from 'preffx/server';
import { App } from './App';
export async function render(url: URL) {
const server = createRoot({ defaultURL: url });
const { preload, serialize } = server.renderToString(App, {});
await preload();
return { appHtml: serialize() };
}// entry-client.tsx
import { createRoot } from 'preffx';
import { App } from './App';
createRoot().mount(App, { node: document.getElementById('app')! });Node sunucusu appHtml'i HTML kabuğuna (örneğin index.html) işler ve tarayıcıya gönderir; ardından istemci betiği aynı ağacı "hydrate" eder (canlandırır).