Svelte to framework, który od lat włada sercami deweloperów. W tym tygodniu ukazał się SvelteKit – przygotowywane od dłuższego czasu renderowanie po stronie Servera dla Svelte. Jeśli jesteście ciekawi jak SvelteKit zamierza powalczyć z Next.js i Nuxt to zapraszamy do kolejnej edycji naszego przeglądu.
1. SvelteKit 1.0
Jest rok 2016 i Rich Harris jest jednym z programistów New York Times. Jednym z jego obowiązków jest przygotowywanie interaktywnych wizualizacji danych na potrzeby publikowanych w sieci artykułów. Ze względu na brak lepszych alternatyw, Rich decyduje się stworzyć własny framework – Reactive.js. Jego jedyną bolączką jest gigantyczna paczka JavaScript wysyłana do klienta. W trakcie jednego z lokalnych meet upów Rich wysłuchuje prezentacji na temat kompilatorów. Ta inspiruje go do tego stopnia, że cały weekend świąt dziękczynienia poświęca na intensywny hackathon. W ostatni wieczór weekendu do sieci trafia Svelte 1.0 – w pełni kompilowany framework JavaScript, wysyłający do klienta minimalne ilości JavaScript.
Svelte szybko podbija serca deweloperów. W State of JS 2021 Svelte po raz drugi z rzędu w kategorii “Usage” zajmuje czwarte miejsce. Spoglądając na kategorię “Retention” i “Interest” nie sposób nie odnieść wrażenia, że Svelte ma to “coś” co kochają deweloperzy. W kategoriach tych od trzech lat Svelte zajmuje jedno z pierwszych dwóch miejsc. Wciąż czekamy na wyniki State of JS 2022, ale stawiam dolary przeciwko orzechom, że Svelte powtórzy swój wynik także i w tym roku.
Dziś nie będziemy jednak mówić o samym Svelte, a projekcie którym zajmował Richa Harris przez ostatnie miesiące. W minionym tygodniu wreszcie ukazała się pierwsza stabilna wersja SvelteKit – frameworku do renderowania Svelte po stronie serwera. Jeśli do tej pory nie mieliście z Svelte styczności, to powinniście zostawić ten artykuł i zająć się nauką tego wspaniałego frameworku jego funkcjonalności postaram się opisać w sposób możliwie najbardziej przystępny dla przeciętnego Frontend Developera.
Routing oparty o strukturę plików
Podobnie jak Remix, Next.js czy Nuxt, tak i SvelteKit zdecydował się oprzeć routing o strukturę katalogów. Od początku mówimy tutaj o pełnym pakiecie możliwości, bo obsługiwane są parametry ([slug]
), opcjonalne fragmenty ([[optional]]
), dopasowywanie fragmentów o dowolnej długości ([...rest]
), a nawet możliwość walidacji typów danych w ścieżce za pomocą spersonalizowanych matcherów ([id=integer]
).
src/routes
├ blog
│ ├ [[author]] 👈 Optional parameter
│ │ ├ [slug] 👈 Dynamic slug
│ │ │ ├ +page.svelte 👈 This will match both /blog/tomasz-borowicz/angular-15
│ and /blog/angular-15
├ cites
│ ├ [id=integer] 👈 Type validator (note: this requires creating ParamMatcher)
│ │ ├ +page.svelte 👈 This will match both /cites/125
│ but not /cites/f8b0fc3e-6421-4722-b420
├ snippets
│ ├ [...filePath] 👈 Rest parameters
│ │ ├ +page.svelte 👈 This will match both /snippets/javascript/hoisting/index.js
│ and /snippets/index.js
Strona w SvelteKit
Na każdą stronę w SvelteKit składają się dwa pliki: +page.js
oraz +page.svelte
. Do tego pierwszego trafić powinna funkcja load()
, która odpowiedzialna będzie za ładowanie danych potrzebnych do wyrenderowania strony. Do tego drugiego powinien natomiast trafić komponent Svelte, który odpowiednio przekształci otrzymane dane do HTMLa.
// src/routes/blog/[id]/+page.ts
import type { getBlogPostById } from '$lib/rest.ts';
import type { PageLoad } from './$types';
export const load = (async ({ params }) => {
return await getBlogPostById(params.id);
}) satisfies PageLoad; // 👈 Note how SvelteKit uses brand new satisfies operator here
// src/routes/blog/[id]/+page.svelte
<script lang="ts">
import type { PageData } from './$types';
export let data: PageData;
</script>
<h2>{data.post.title}</h2>
<div>{data.post.content}</div>
W domyślnym trybie, SvelteKit wyrenderuje pierwszą stronę na serwerze, a następnie przekaże renderowanie do klienta. Możemy jednak zmodyfikować to zachowanie zmieniając rozszerzenie pliku na server.js
/server.svelte
. W takiej sytuacji SvelteKit zadba o to, żeby taki kod zawsze wykonywany był po stronie serwera. To z kolei daje nam możliwość prostego odnoszenia się do serwerowych API.
// src/routes/blog/[id]/+page.server.ts
import * as db from '$lib/server/database';
import type { PageLoad } from './$types';
export const load = (async ({ params }) => {
return await db.getPostById(params.id); // 👈 Note you can acces server API here
}) satisfies PageLoad;
Za pomocą kilki zmiennych eksportowanych z komponentu możemy też sterować strategią renderowania
// src/routes/about/+page.svelte
<script lang="ts">
export const prerender = true // render at build time
export const ssr = false // disable server rendering
export const csr = false // disable JavaScript hydration
</script>
<h2>About</h2>
<p>This is a very cool blog of a very cool company</p>
Layout i obsługa błędów
SvelteKit już w pierwszej wersji posiada funkcjonalności na które w innych frameworkach czekaliśmy nawet do wersji 13 (tak, na Ciebie patrzę Next.js 😈). Mowa tu oczywiście o layouts i errors, które osadzamy na dowolnym poziomie struktury katalogów w plikach +layout.svelte
i +error.svelte
. Co ważne, podobnie jak w przypadku samych stron, tak i tutaj możemy zapewnić, że dany kod będzie wykonywany tylko po stronie serwera odpowiednio modyfikując rozszerzenie pliku.
// src/routes/blog/+layout.svelte
<script lang="ts">
import type { LayoutData } from './$types';
export let tabs = [ /*...*/];
</script>
<h2>Settings</h2>
<div class="submenu">
{#each tabs as tab}
<a href="{tab.href}">{tab.title}</a>
{/each}
</div>
<slot></slot> // 👈 All the pages in /blog will be rendered in place of this slot
// src/routes/blog/+error.svelte
<script>
import { page } from '$app/stores';
</script>
<h2>{$page.status}: {$page.error.message}</h2>
Obsługa formularzy
SvelteKit oferuje też bardzo oryginalne (czyt. skopiowane od Remix) rozwiązanie jeśli chodzi o obsługę formularzy. W pliku +page.js
oprócz funkcji load()
, możemy umieścić również obiekt actions()
. Będzie on odpowiedzialny za przetwarzanie wypełnionych formularzy.
// src/routes/login/+page.ts
import type { PageServerLoad, Actions } from './$types';
export const load = (async ({ cookies }) => {
const user = await db.getUserFromSession(cookies.get('sessionid'));
return { user };
}) satisfies PageServerLoad;
export const actions: Actions = {
login: async ({ cookies, request }) => {
const data = await request.formData();
const email = data.get('email');
const password = data.get('password');
const user = await db.getUser(email);
cookies.set('sessionid', await db.createSession(user));
return { success: true };
}
};
Następnie w pliku page.svelte
możemy dodać do formularza prametr action
. W efekcie nasza aplikacja w momencie zaakceptowania formularza będzie wysyłać zapytanie typu POST na serwer, gdzie wywoływana będzie opowiednia metoda z obiekty actions
. Co w tym wyjątkowego? Otóż dzięki zastosowaniu natywnych mechanizmów przeglądarki, taki formularz będzie w pełni funkcjonalny nawet po wyłączeniu przez użytkownika JavaScript!
// src/routes/login/+page.svelte
<script lang="ts">
import type { PageData, ActionData } from './$types';
export let form: ActionData;
</script>
<form method="POST" action="?/login">
<label>
Email
<input name="email" type="email">
</label>
<label>
Password
<input name="password" type="password">
</label>
<button>Log in</button>
<button formaction="?/register">Register</button>
</form>
{#if form?.success}
<p>Successfully logged in! Welcome back, {data.user.name}</p>
{/if}
Backend for Frontend
Skoro SvelteKit obsługuje zapytania POST przy okazji obsługi formularzy, to dlaczego nie miałby obsługiwać również innych zapytań? Podobne pytanie zadali sobie twórcy frameworka i w efekcie w SvelteKit możemy definiować własne RESTowe endpointy
// src/routes/api/random-number/+server.ts
import { error } from '@sveltejs/kit';
import { random } from 'lodash/es';
import type { RequestHandler } from './$types';
// You can call this endpoit with PATCH /api/random-number?min=5&max=8
export const PATCH = (({ url }) => {
const value = random(url.searchParams.get('min'), url.searchParams.get('min'), true);
return new Response(String(value));
}) satisfies RequestHandler;
Preloading
Jednym z bardzo popularnych wzorców optymalizujących czas ładowania JavaScript jest ładowanie tylko kodu potrzebnego do wyrenderowania obecnej strony. Niestety odbywa się to kosztem doładowywania kolejnych plików w momencie kiedy użytkownik chce przejść na inną stronę. W takiej sytuacji użytkownik zapewne doświadczy lekkiego chrupnięcia. I w takiej sytuacji SvelteKit oferuje lekarstwo. Anotując odpowiednio element <a>
Svelte rozpocznie ładowanie potrzebnego kodu i danych już w momencie kiedy kursor użytkownika zacznie zmierzać w jego kierunku.
<a data-sveltekit-preload-data="hover" href="/page-A">
This page code will load when cursor hovers over the link
</a>
<a data-sveltekit-preload-data="viewport" href="/page-B">
This page code will load when the link enters the viewport
</a>
Deployment SvelteKit
Rich Harris już od dłuższego czasu jest pełnoetatowym pracownikiem Vercela – firmy rozwijającej Next.js i zarabiającej głównie na swojej chmurze. Nie dziwi więc, że SvelteKit 1.0 już od dnia zero można jednym kliknięciem deploy’ować na chmurę Vercela. Na szczęście nie jest to vendor lock in jak ten znany z Next.js i SvelteKit bez większych problemów hostować możecie na dowolnej chmurze obsługującej Node.js
Źródła:
https://svelte.dev/blog/announcing-sveltekit-1.0
Zainstaluj teraz i czytaj tylko dobre teksty!
2. Vite 4.0
Vite jest jedną z najszybciej wschodzących gwiazd naszej frontendowej społeczności. Początkowo był on narzędziem dedykowanym do budowania aplikacji napisanych we Vue. Rewolucja nadeszła dopiero wraz z opublikowaniem Vite 2.0, kiedy to stał się on narzędziem framework-agnostic. Szybkość narzędzia robiła wrażenie, więc nic dziwnego, że z czasem zaczęła kusić coraz więcej React deweloperów i nie tylko. Vite stał się tak popularny, że bardzo szybko skanalizował właściwie całą swoją konkurencję (RIP Snowplow).
W minionym tygodniu opublikowany został Vite 4.0. Jest to o tyle zaskakujące, że niespełna pół roku temu ukazał się Vite 3.0. Pamiętajcie jednak, że Vite przestrzega semantic versioning. W tym wypadku duży numerek jest związany przede wszystkim z braking changes, a nie nowymi funkcjonalnościami. Źródłem breaking changes jest przede wszystkim przesiadka z Rollup 2.0 na Rollup 3.0.
We wstępie do tej sekcji napisałem, że Vite skanalizował całą swoją konkurencję. W tym roku pojawił się jednak nowy pretendent do korony – Turbopack (napisany w Rust przez byłego twórcę Webpack). Na razie znajduje się on we wczesnej alfie i jak donoszą pierwsi śmiałkowie z Twitter nie działa jeszcze najlepiej. Nie przeszkodziło to jednak twórcom pochwalić się, że działa on prawie 4x szybciej niż Vite. Evan You (twórca Vue i Vite) szybko zaczął przyglądać się tym liczbom. Jak się okazało większość z tej przewagi wynikała z zastosowania SWC zamiast Babela i lekkiego przypudrowania wyników. Kurz po “aferze wydajnościowej” trochę już opadł, a Vite 4.0 dodaje wsparcie dla SWC. Jestem ciekaw jak teraz prezentuje się zestawienie Vite i Turbopack.
Najwięcej komentarzy na Reddicie pod postem ogłaszającym Vite 4.0 dotyczyło zabawy w rozpoznawanie logo na jednej z grafik w poście. Ja też zapraszam Was do tej zabawy – ile projektów na poniższej grafice jesteście w stanie rozpoznać?