Svelte

**Svelte** on ohjelmistokehys web-sovellusten kehittämiseen JavaScriptin, HTML:n ja CSS:n avulla. Sille on ominaista muun muassa nopeus ja vakiokoodin () ja erikoissyntaksin vähyys. Nopeus on seurausta erityisesti siitä, että Svelte kääntää sovelluskoodin natiiviksi JavaScriptiksi ja pyrkii näin tekemään vähemmän työtä ajon aikana. Muita vastaavia kehyksiä ovat muun muassa React ja Vue.  

Svelte tukee suoraan myös TypeScriptiä ja SCSS:ää, jotka käännetään JavaScriptiksi ja CSS:ksi esiprosessointivaiheessa.

## Esimerkit

### SvelteKit-demo

SvelteKit-kehyksen sisältämän demosovelluksen tiedostorakenne näyttää seuraavalta:

```
$ tree --filelimit 20
├── node_modules  [116 entries exceeds filelimit, not opening dir]
├── package.json
├── package-lock.json
├── README.md
├── src
│   ├── app.html
│   ├── lib
│   │   └── images
│   │       ├── github.svg
│   │       ├── svelte-logo.svg
│   │       ├── svelte-welcome.png
│   │       └── svelte-welcome.webp
│   └── routes
│       ├── about
│       │   ├── +page.js
│       │   └── +page.svelte
│       ├── Counter.svelte
│       ├── Header.svelte
│       ├── +layout.svelte
│       ├── +page.js
│       ├── +page.svelte
│       ├── styles.css
│       └── sverdle
│           ├── how-to-play
│           │   ├── +page.js
│           │   └── +page.svelte
│           ├── +page.server.js
│           ├── +page.svelte
│           └── words.server.js
├── static
│   ├── favicon.png
│   └── robots.txt
├── svelte.config.js
└── vite.config.js
```


Tämä demosovellus sisältää laskurikomponentin tiedostossa `Counter.svelte`, joka näyttää oleellisilta osin seuraavalta:

```html
<script>
    // Tuo store-objekti, jonka avulla voidaan muuttaa
    // arvo toiseen jatkuvalla tavalla.
	import { spring } from 'svelte/motion';
    // Komponentissa on yksi tilamuuttuja, joka
    // saa kokonaislukuarvoja.
	let count = 0;

    // Käyttäjälle näytetään reaalilukuja.
	const displayed_count = spring();
    // $-nimellä merkityt lauseet ovat reaktiivisia sisältämiensä
    // muuttujien arvojen muutoksille.
	$: displayed_count.set(count);
    // Jos muuttujan nimi alkaa $:lla, se tulkitaan automaattisesti
    // store-objektiksi tavallisen muuttujan sijaan.
	$: offset = modulo($displayed_count, 1);

    // Alla olevaa funktiota käytetään yllä; tämä on sallittua JavaScriptissä 
    // deklaraationa määritellyille funktioille ().
	function modulo(n, m) {
		return ((n % m) + m) % m;
	}
</script>

<div class="counter">
    
	<button on:click={() => (count -= 1)} aria-label="Decrease by one">
        
	</button>

	<div class="cv">
        
		<div class="cd" style="transform: translate(0, {100 * offset}%)">
			<strong class="hidden" aria-hidden="true">
                {Math.floor($displayed_count + 1)}
            </strong>
			<strong>
                {Math.floor($displayed_count)}
            </strong>
		</div>
	</div>

	<button on:click={() => (count += 1)} aria-label="Increase by one">
        
	</button>
</div>

<style>
    /* CSS poistettu */
</style>
```


## Käyttö

Svelten käyttö onnistuu esim. SvelteKit-työkalun avulla `npm create svelte@latest nimi`. Tämä edellyttää Node.js-ajoympäristöä ja npm-pakettienhallintaohjelmaa. `npm install` asentaa sovelluksen riippuvuudet ja `npm run dev` käynnistää paikallisen kehityspalvelimen. `npm run build` tuottaa lopullisen sovelluksen konfiguraatiotiedostojen mukaan ja tämä voidaan jakaa halutulla alustalla Node-palvelimella. Osana tätä rakennusprosessia Svelte kääntää tiedostonsa optimoiduksi, natiiviksi JavaScriptiksi (luokaksi).

## Rakenne

### Komponentit

Svelte-sovellukset koostuvat komponenteista, jotka ovat itsenäisiä joukkoja JavaScript-, HTML-, CSS-kieltä ja Svelte-syntaksia. Komponentit määritellään omiin tiedostoihinsa. JavaScript tulee `script`-merkkien väliin ja CSS `style`-merkkien väliin – loput komponentista on Svelten laajennettua HTML-tyyppistä merkintäkieltä. JavaScriptiä voidaan sekoittaa suoraan merkintäkieleen kaarisulkeissa, kuten `` tai `<img {src} alt="Kuvateksti." >`. Samalla tavalla esim. `style`-direktiivi helpottaa CSS-tyylien lisäämistä suoraan elementteihin tyylillä ``.

Toisessa tiedostossa esim. `Komponentti.svelte` määritelty komponentti saadaan käyttöön `import`-lauseella, minkä jälkeen sitä voidaan käyttää HTML-elementin tavoin `<Komponentti/>`. Kun muuttujan (tai lauseen) tulee olla reaktiivinen toisen muuttujan arvon muutokseen, merkitään haluttu lause nimellä `$`, kuten `$: x = teeJotain(y);`. Vain muuttujaan sijoittaminen tekee muuttujasta reaktiivisen, eli JavaScript-objektin mutaatio muulla tavalla ei päivitä sovelluksen tilaa. 

Komponentteja määritellessä käytetään `slot`-elementtiä tyylillä `` kertomaan Sveltelle, mihin komponentin alikomponentit/elementit, kuten `` injektoidaan. Jos mitään injektoitavaa ei anneta ``, Svelte käyttää komponenttiin määriteltyä oletuselementtiä `<slot><nimi>...</nimi></slot>`. Jos komponentissa on taas useita slotteja, niille annetaan nimet `<slot name="nimi">` ja injektoitava elementti ohjataan sinne attribuutilla ``.

Logiikan lisääminen suoraan komponentin rakenteeseen onnistuu
* ehtolauseilla `{#if ...} ... {:else if ...} ... {:else} ... {/if}` tai `{#key arvo} ... {/key}`
* toistolauseella `{#each xs as x (x.id)} ... {/each}` ja
* asynkroniselle tulokselle (`Promise`) syntaksilla `{#await x} ... {:then y} ... {:catch e} ... {/await}` tai lyhyemmin `{#await x then y} ... {/await}`.

Erityisiin sisäänrakennettuihin Svelte-elementteihin kuuluvat `<svelte:self>`, `<svelte:component this=...>`, `<svelte:element this=...>`, `<svelte:window>`, `<svelte:body>`, `<svelte:head>`, `<svelte:options>` (kääntäjän asetukset) ja `<svelte:fragment>`.

### Komponenttien tiedonkulku

Kun alikomponentti tuodaan uuteen komponenttiin, tämän näkymään siirtyvät samalla komponentista jaetut muuttujat eli ns. propsit (), kuten `export let muuttuja = "oletusarvo";`. Myös slotit hyväksyvät propseja, mutta nämä tulee tuoda näkyviin injektoinnin yhteydessä `let`-direktiivillä, kuten ``. Tällöin arvo siirtyy siis toiseen suuntaan ylhäältä alas.

Komponenttien kesken voidaan jakaa tietoa myös ilman *props*-mekanismia kontekstien avulla. Kun komponentti kutsuu `setContext`-funktiota, kyseinen konteksti (mikä tahansa objekti) on saatavissa alikomponenteissa `getContext`-kutsulla. Kolmas tapa jakaa tietoa saman komponentin instanssien kesken on käyttää ns. moduulikontekstia: kaikki ``-merkkien sisällä oleva JavaScript ajetaan vain kerran moduulin ajon aikana, eikä jokaiselle instanssille erikseen.

Käyttäjän antamien arvojen siirtämiseksi ylös komponenttihierarkkiaa tarvitaan `bind`-direktiiviä, kuten ``. Eri elementeillä on erilaisia attribuutteja, joiden kanssa voidaan käyttää `bind`-direktiiviä joko yksi- (lue) tai kaksisuuntaisesti (lue ja kirjoita). `bind:this` viittaa attribuutin sijasta itse elementtiin, ja useasta elementistä voidaan ohjata arvoja yhteen muuttujaan käyttämällä `bind:group`-attribuuttia.

Komponenttien ulkopuolisten tilamuuttujien käyttämiseen tarvitaan ns. tietovarastoa (), joka luodaan funktioiden, kuten `writable`, `readable` ja `derived` avulla tyylillä `export const x = writable(0);`. Tätä muuttujaa käytetään sitten tuonnin jälkeen sen metodien, kuten `subscribe`, `set` ja `update` kautta. Metodien suora käyttö voidaan myös välttää dollarisyntaksilla `$muuttuja` – Svelte tulkitsee tällaisen nimen aina varastoksi.

### Tapahtumienhallinta

Tapahtumankäsittelijän liittämiseksi komponentin/elementin tapahtumaan tehdään `on`-direktiivin avulla. Direktiivin ja tapahtuman lisäksi voidaan käyttää määreitä () muuttamaan tapahtumankäsittelijän toimintaa syntaksilla ``. Tapahtumankäsittelijä voi olla yksinkertaisen funktion sijasta myös ns. tapahtumanlähettäjä (), jonka luomiseen käytetään Svelten `createEventDispatcher`-funktiota. Omien tapahtumien käsittelemiseksi tarvitaan oma ns. toimintofunktio (), joka liitetään elementtiin `use`-direktiivillä, kuten ``.

Tapahtumat eivät nouse () komponenttihierarkiassa ylös automaattisesti; ulompaan komponenttiin ei kuitenkaan tarvitse määritellä omaa edelleenlähetystä vaan pelkän attribuutin lisääminen riittää ``. 

### Elinkaari ja siirtymät

Svelten elinkaarifunktio `onMount` mahdollistaa JavaScriptin ajon, kun komponentti renderöidään ensimmäisen kerran. Muita elinkaarifunktioita ovat `onDestroy`, `beforeUpdate`, `afterUpdate` ja `tick` (esim. `await tick(); teeJotain();`.

Animaatioiden tekemiseen löytyy työkaluja `motion`, `easing`, `transition` ja `animate` moduuleista, kuten `tweened`, `spring`, `fade`, `crossfade` ja `flip`. DOM-siirtymät sidotaan elementteihin `transition`-, `in`-, `out` ja `animate`-direktiivien avulla, kuten ``. Siirtymillä voi olla myös määreitä, kuten `transition:slide|local`. 

## Lähteet