Skip to content

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.

ts
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 script

Genellikle 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:

ts
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çenekTürAçıklama
defaultURLstring | URLYönlendirme (routing) için başlangıç ​​URL'si
defaultLangstringUluslararasılaştırma (i18n) için başlangıç ​​dili
prefixstringKök bazlı önek (istemci köküyle eşleşmelidir)
contextobjectAğ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 (PC veya APC).
  • 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İmzaAçıklama
preloadpreload(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.
serializeserialize(): stringHTML 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.

tsx
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 önce user.state.value değeri null, pending ise true durumundadı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ıca serialize() 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 pending değeri false olarak 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.

tsx
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 createRoot seçenekleri (prefix, defaultLang, defaultURL, context), sunucu tarafındakilerle eşleşmelidir.
  • Oluşturulan işaretleme (markup), tam olarak istemcinin beklediği yere (mount ile referans verilen kapsayıcıya) yerleştirilmelidir.

Örnek: Tam istek akışı ​

tsx
// 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() };
}
tsx
// 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).

Apache Lisansı 2.0 altında yayınlanmıştır