Bosia Bosia v1.2.9

Navigasi

Navigasi programatik dan berbasis tautan di Bosia — goto(), beforeNavigate, afterNavigate, form actions, dan escape hatch full-reload.

Bosia menyediakan empat pola navigasi yang mencerminkan SvelteKit:

  1. Tautan <a href> biasa — router mencegatnya.
  2. goto() — navigasi SPA programatik.
  3. Form action redirect(303, '/x') — navigasi pasca-submit yang digerakkan server.
  4. window.location.href — reload browser penuh (escape hatch).

Tiga yang pertama bersifat SPA — tidak mengeksekusi ulang skrip entry. Yang keempat membongkar dan menghidrasi ulang.

Tautan anchor

<a href="/dashboard">Dashboard</a>

Handler klik milik Bosia mencegat klik anchor biasa dan menjalankan router.navigate(). Klik dengan modifier (Cmd, Ctrl, klik tengah) dan target="_blank" diteruskan ke browser, sehingga "buka di tab baru" tetap bekerja seperti yang diharapkan. rel="external" dan download juga ikut keluar, dan prefetch saat hover juga melewatinya, jadi loader /logout tidak pernah jalan saat hover. Halaman yang dibuka dari file:// (tanpa host) selalu memuat ulang penuh; aplikasi Electron yang ingin navigasi klien sebaiknya disajikan dari skema dengan host, seperti app://bundle.

`goto()`

import { goto } from "bosia/client";

await goto("/dashboard");
await goto("/login", { replaceState: true, invalidateAll: true });

goto() mengembalikan sebuah Promise yang resolve setelah navigasi mengendap (loader berjalan, komponen ter-mount).

URL relatif di-resolve terhadap halaman saat ini, seperti tautan: goto("?page=2") tetap di dalam aplikasi. goto("#x") adalah navigasi di halaman yang sama. URL javascript:, data: atau vbscript: membuat Promise-nya reject, sama seperti redirect() yang throw. Situs lain tetap diizinkan, jadi pastikan nilai ?next= tetap di situs Anda sebelum dipakai.

Opsi

Opsi Default Deskripsi
replaceState false Memakai history.replaceState alih-alih pushState.
invalidateAll false Menandai setiap loader kotor agar tujuan menjalankan ulang semua server loader.
noScroll false Melewati scroll-to-top default setelah navigasi.
keepFocus false Dicadangkan — belum dihormati.
state — Dicadangkan — Bosia belum punya shallow routing.

Jika URL cocok dengan route saat ini, goto() resolve tanpa menjalankan ulang loader — langsung, atau setelah navigasi ke URL itu yang masih memuat selesai. Berikan invalidateAll: true untuk menjalankannya ulang di tempat; Promise resolve setelah data baru tampil di halaman.

Di bawah `BASE_PATH`

Jika aplikasi dipasang di sub-path, goto() menerima path asli yang sudah berprefix. Tabel rute dihasilkan dengan prefix dan router tidak mengonversi apa pun, jadi path ruang-aplikasi tidak cocok dengan rute mana pun dan jatuh ke full page load ke akar origin:

import { base } from "bosia";

await goto(`${base}/dashboard`); // ✅
await goto("/dashboard"); // ❌ keluar dari aplikasi saat BASE_PATH disetel

base bernilai "" saat aplikasi berjalan di akar origin, jadi menuliskannya selalu aman. Literal <a href="/dashboard"> tidak perlu diubah — markup ditulis ulang untuk Anda.

Saat development, navigasi yang seharusnya cocok dengan prefix akan mencatat peringatan berisi path yang benar, alih-alih diam-diam keluar dari aplikasi:

[bosia] "/dashboard" matched no route and is leaving the app — it is mounted at "/sso". Use "/sso/dashboard".

Halaman saat ini

page dari bosia/client memberi tahu komponen di mana ia berada. Nilainya berubah di setiap navigasi dan sudah benar sejak SSR.

<script lang="ts">
	import { page } from "bosia/client";
</script>

<a href="/admin" class:active={page.url.pathname.startsWith("/admin")}>Admin</a>
{#if page.route.id?.startsWith("/(private)")}<SignOutButton />{/if}
  • page.url — URL saat ini.
  • page.route.id — folder rute dengan grup dan parameter tetap ada, mis. /(private)/dashboard/[id]. Id yang sama dengan yang dilihat hook dan loader. null bila tidak ada rute yang cocok (halaman 404).

Parameter rute datang sebagai prop params, bukan di page.

Perilaku scroll

  • Navigasi maju (link, goto(), redirect form) scroll ke atas — atau ke elemen #hash jika URL tujuan memilikinya. goto(url, { noScroll: true }) melewatinya untuk satu navigasi.
  • Back/forward (popstate) memulihkan posisi scroll halaman saat ditinggalkan. Router menyimpan posisi per entri history dan memulihkannya setelah halaman tujuan dirender, jadi listing panjang tidak melompat ke atas saat pengguna menekan Back. Posisi bertahan melewati reload via sessionStorage.
  • Refresh di halaman yang sama (invalidate(), invalidateAll(), form use:enhance yang tetap di halaman) mempertahankan posisi scroll saat ini.
  • await goto() lanjut setelah scroll ini, jadi window.scrollTo() tepat setelahnya tidak tertimpa.

Ini otomatis — tanpa kode aplikasi.

Snapshot

Scroll restoration mengembalikan posisi; snapshot mengembalikan state — isi form yang belum dikirim, accordion yang terbuka, kotak pencarian. Export objek snapshot dari +page.svelte:

<script lang="ts">
	import type { Snapshot } from "./$types";

	let comment = $state("");

	export const snapshot: Snapshot<string> = {
		capture: () => comment,
		restore: (value) => (comment = value),
	};
</script>

<textarea bind:value={comment}></textarea>

capture() berjalan saat pengguna meninggalkan halaman; restore(value) berjalan saat mereka kembali via tombol Back/Forward browser. Snapshot juga bertahan melewati reload penuh — dipersist ke sessionStorage saat unload.

Aturan:

  • Nilai yang di-capture harus bisa diserialisasi JSON (tanpa fungsi, Date jadi string) — begitulah cara ia bertahan melewati reload.
  • Navigasi maju ke halaman tidak pernah me-restore — hanya Back/Forward (dan reload), sama seperti SvelteKit.
  • Hanya +page.svelte yang mendukung snapshot (bukan layout).

Snapshot<T> juga bisa diimpor dari bosia/client.

Lifecycle hooks

beforeNavigate berjalan sebelum setiap navigasi sisi-klien. Callback boleh memanggil nav.cancel() untuk memblokir navigasi (kecuali pada back/forward browser — lihat di bawah).

<script lang="ts">
	import { beforeNavigate, afterNavigate } from "bosia/client";

	beforeNavigate((nav) => {
		if (hasUnsavedChanges && !confirm("Discard changes?")) {
			nav.cancel();
		}
	});

	afterNavigate((nav) => {
		console.log("navigated to", nav.to?.url.pathname);
	});
</script>

Keduanya otomatis tidak terdaftar lagi saat komponen pemanggil dihancurkan.

Objek Navigation

interface Navigation {
	from: { url: URL; params: Record<string, string> } | null;
	to: { url: URL; params: Record<string, string> } | null;
	type: "link" | "goto" | "popstate" | "form" | "enter";
	willUnload: boolean;
	cancel: () => void;
}
  • type — bagaimana navigasi dipicu.
  • willUnload — true saat browser akan membongkar halaman (tab ditutup, tautan eksternal). Untuk peristiwa ini cancel() tidak berefek; gunakan addEventListener("beforeunload", ...) langsung untuk meminta konfirmasi pengguna.
  • cancel() pada navigasi popstate (back/forward) juga tidak berefek — browser sudah memindahkan pointer history-nya saat listener berjalan.
  • Redirect dari loader adalah navigasi tersendiri bertipe goto (atau link/form, mengikuti navigasi sebelumnya), juga setelah Back — jadi cancel() berlaku.

Indikator Loading

Bosia menampilkan bilah progres tipis di bagian atas halaman selama setiap navigasi klien. Untuk transisi yang lebih kaya, letakkan +loading.svelte di folder route dan Bosia merender kerangka itu secara otomatis saat tujuan dimuat — tertanam di dalam layout yang dibagi, tanpa perlu mengatur beforeNavigate. Lihat Kerangka Loading.

Form actions

Form action yang melempar redirect(303, "/x") bernavigasi melalui router secara otomatis saat dibungkus dengan use:enhance. Semua loader dijalankan ulang (login atau logout juga mengubah data layout), jadi redirect ke halaman yang sama menampilkan data terbaru:

<form method="POST" action="?/logout" use:enhance>
	<button>Log out</button>
</form>

Lihat Form Actions untuk cerita lengkapnya.

`window.location.href` — reload penuh

Gunakan hanya saat Anda benar-benar perlu membongkar semua state klien (mis. logout, berganti tenant di aplikasi multi-tenant ketika konteks runtime yang segar diinginkan):

function hardLogout() {
	document.cookie = "session=; Max-Age=0";
	window.location.href = "/";
}

Untuk navigasi internal, lebih baik goto() — lebih cepat (tanpa parse ulang skrip, tanpa biaya hidrasi ulang) dan mempertahankan cache loader sebisa mungkin.