Island server

Kadang satu bagian dari halaman yang selebihnya statis butuh data segar per permintaan — komentar di bawah pos blog yang disinggahkan, lencana stok di halaman produk. Island server membiarkan halaman tetap statis dan merender hanya bagian itu di server ketika halamannya dilihat.

1. Tulis island-nya

Island adalah modul fragmen di bawah src/islands/ — berkas .js atau .ts biasa (bukan .ht.js, karena island adalah fragmen, bukan halaman). Gagasannya sama seperti di semua tempat lain di sitelo: fungsi yang mengembalikan HTML.

export default async function comments({ props, request }) {
  const comments = await fetchComments(props.postId)
  return `<ul>${comments.map((c) => `<li>${c.text}</li>`).join('')}</ul>`
}

Ia menerima { name, props, request } dan harus mengembalikan string HTML. Modul island hanya untuk server — kode yang tidak dirujuk di bawah src/ tidak pernah sampai ke peramban.

2. Tempatkan di sebuah halaman

Impor island() dari sitelo/islands. Build statis mengirim HTML cadangannya; props disematkan di penampung, jadi buatlah kecil dan bukan rahasia.

import { html, body, article, script } from 'javascript-to-html'
import { island } from 'sitelo/islands'

export default ({ params }) =>
  html(
    body(
      article('…konten statis…'),
      island('comments', { postId: params.slug }, '<p>Memuat komentar…</p>'),
      script({ type: 'module', src: '/islands.js' }),
    ),
  )

3. Tambahkan pemuat klien

Skrip mungil mengambil tiap fragmen hasil render dan menukarnya masuk. Ia melewati jalur aset biasa, jadi entri src/islands.js sederhana sudah cukup:

import { mountIslands } from 'sitelo/islands/client'

mountIslands()

Di sitelo (pengembangan) dan sitelo preview ini sudah berfungsi — keduanya menyajikan island di /_sitelo/islands/<name> dari src/islands/. Preview memuat modul .js / .mjs asli (sama seperti host Node); island TypeScript didukung saat pengembangan lewat Vite.

Produksi

Hosting statis Anda tetap menyajikan halamannya. Pasang penangan kecil di mana pun Anda menjalankan kode server — Node, tanpa server, atau fungsi edge — dan ia merender modul island yang sama. Untuk penelusuran lengkap dengan host Node yang bisa dijalankan plus kerangka Netlify dan Vercel, lihat contoh Island server.

// misalnya server Node, atau fungsi serverless/edge
import { createIslandsHandler } from 'sitelo/islands/server'

const handleIslands = createIslandsHandler({
  islands: {
    comments: () => import('./src/islands/comments.js'),
  },
})

// Request web → Response | null (null = bukan permintaan island)
export default { fetch: (request) => handleIslands(request) }

Pada Node http biasa atau express, gunakan createIslandsNodeHandler(options) sebagai gantinya — opsi yang sama, tanda tangan (req, res, next). Sambungkan otomatis setiap modul .js / .mjs di bawah src/islands/ dengan createIslandsFromDirectory. Jika pemuat mengambil dari asal atau jalur berbeda, berikan mountIslands({ endpoint: 'https://api.example.com/islands' }) dan cocokkan dengan opsi endpoint pada penangannya.

Daftar periksa kestabilan

Strategi pemuatan

Secara bawaan setiap island mengambil begitu halaman dimuat, jadi halaman dengan delapan island membuat delapan permintaan serentak saat lukisan pertama. Berikan when untuk menunda yang tidak langsung terlihat.

// Muat begitu halaman dimuat — bawaannya.
island('cart', { id }, '<p>…</p>')

// Tunggu callback saat menganggur.
island('recommendations', { id }, '<p>…</p>', { when: 'idle' })

// Tunggu sampai tergulir ke tampilan.
island('comments', { postId }, '<p>Memuat komentar…</p>', {
  when: 'visible',
  rootMargin: '400px',   // mulai memuat 400px lebih awal
})

rootMargin hanya berlaku untuk 'visible' dan bawaannya '200px'. Island juga kehabisan waktu alih-alih berputar selamanya:

mountIslands({
  timeout: 5000,        // per island; 0 menonaktifkan. Bawaan 10000
  rootMargin: '300px',  // bawaan untuk island `when: 'visible'`
})

mountIslands() selesai begitu island langsung sudah tenang — yang ditunda dimuat belakangan sendiri dan sengaja tidak ditunggu. Island yang gagal atau kehabisan waktu mempertahankan HTML cadangannya.

Props adalah masukan tak tepercaya

Props island berasal dari klien. Mereka disematkan di halaman, dikirim balik pada permintaan, dan siapa pun bisa menyuntingnya lebih dulu:

GET /_sitelo/islands/profile?props={"userId":"someone-else"}

Perlakukan props yang diterima island Anda persis seperti parameter kueri — validasi, dan jangan pernah memakainya untuk mencari data yang memang belum berhak dilihat pengunjung.

Ketika props memilih data istimewa, tanda tangani. Setel sebuah rahasia dan sitelo menandatangani setiap penampung saat build, menolak yang lain dengan 403:

SITELO_ISLANDS_SECRET=$(openssl rand -hex 32) sitelo build

Variabel yang sama dibaca oleh sitelo, sitelo preview, dan createIslandsHandler, jadi pengembangan, preview, dan produksi sepakat. Beri host produksi Anda rahasia yang sama. Lebih suka menyetelnya di kode?

import { configureIslands } from 'sitelo/islands'

configureIslands({ secret: process.env.MY_SECRET })

Tanda tangan berupa HMAC-SHA256 atas nama island dan props-nya, jadi tanda tangan yang diterbitkan untuk satu island tidak bisa diputar ulang terhadap island lain. Penandatanganan membuktikan props berasal dari build Anda — ia tidak menyembunyikannya, jadi props tetap harus bukan rahasia. Tanpa rahasia, props diterima apa adanya dan memvalidasinya sepenuhnya menjadi tugas modul island Anda.

Baik untuk diketahui