Якщо ви досі не працювали зі Svetle, то це чудова можливість почати. Матеріал призначений для розробників, які раніше працювали з фреймворком на зразок Vue чи React і шукають новий підхід. Svelte вартий уваги, тому що він відрізняється від інших фреймворків і має унікальні фічі для вебу.
Вступ до Svelte
Svelte — веб-фреймворк, який пропонує свіжий погляд на те, як створювати веб застосунки.

Якщо ви вже мали досвід з React, Vue, Angular чи іншим фронтенд-фреймворком, Svelte може вас приємно здивувати.
Після знайомства зі Svelte вам здаватиметься, що це, скоріше, чистий JavaScript. Звичайно, є деякі відмінності, на зразок шаблонів, які більше нагадують HTML. Однак зі Svelte ви забудете про труднощі, які виникали в інших фреймворках.
На відміну від React, Vue, Angular та інших фреймворків, застосунок на Svelte заздалегідь компілюється. Так значно покращується користувацький досвід, адже інтерфейс працює плавно. При розгортанні вашого застосунку ви отримуєте чистий (та швидкий) JavaScript.
Початок роботи зі Svelte
Для початку необхідно встановити Node.js, на якому базуються всі інструменти. Для цього скористайтесь посібником і переконайтесь, що маєте останню версію (як оновити Node.js).
Якщо ви не хочете встановлювати Node, веб-сайт Svelte пропонує інтерактивне середовище програмування. Там зручно тестувати невеликі застосунки на Svelte та експериментувати.
З Node встановлюється зручна команда npx
. Одразу використаємо її:
npx degit sveltejs/template firstapp
Так ми налаштуємо degit
, який завантажить останню версію шаблона проєкту Svelte з репозиторію в щойно створену теку firstapp
.
Переконайтеся, що на комп'ютері встановлений git, а також визначена PATH-змінна. Інакше degit
не працюватиме. Якщо ж після всіх кроків у вас все ще не працює проєкт, ви можете клонувати його з репозиторію, а потім видалити приховану теку .git
. По суті, це те ж саме, що робить degit
(різниця лише у тому, що тека називатиметься template
, а не firstapp
).
Перейдемо до теки firstapp
та запустимо npm install
, щоб завантажити додаткові залежності проєкту. Під час написання матеріалу, залежності були такі:
"npm-run-all"
"rollup"
"rollup-plugin-commonjs"
"rollup-plugin-livereload"
"rollup-plugin-node-resolve"
"rollup-plugin-svelte"
"rollup-plugin-terser"
"svelte"
Як бачимо, це основні пакети Svelte, а також Rollup(альтернатива Webpack) та деякі його плагіни. npm-run-all
— CLI-інструмент, який дозволяє запускати декілька npm-скриптів паралельно чи послідовно.
Ми вже готові запустити наш проєкт на Svelte в режимі розробки, виконавши:
npm run dev
Застосунок за замовчуванням запускається на localhost з портом 5000:

Якщо відкрити зазначену адресу у браузері, можна побачити приклад «Hello world» :

Відкриємо код в улюбленому редакторі. В теці src
є файл main.js
з основними налаштуваннями застосунку:

Це точка входу застосунку, а також ініціалізація компонента App, визначеного у файлі App.svelte
:
<script>
export let name;
</script>
<style>
h1 {
color: purple;
}
</style>
<h1>Hello {name}!</h1>
Компоненти Svelte
Сучасна веб-розробка орієнтована на компонентний підхід, Svelte також.
Що таке компонент? Компонент — атомарна та автономна частина застосунку, яка за необхідністю посилається на інші компоненти для формування інтерфейсу.
Іншими словами, це окремий складник застосунку. Компонентом може бути форма, поле вводу або ж увесь застосунок.
Компоненти Svelte містять все необхідне для відображення частини UI. Кожен компонент визначений у файлі з розширенням .svelte
. Там є розмітка (HTML), поведінка компонента (JavaScript), та його зовнішній вигляд (CSS). І все це без потреби створювати окремі файли.
Такий підхід до організації компонента досить зручний, адже немає потреби шукати елементи по декількох файлах.
Розглянемо приклад компонента у файлі Dog.svelte
:
<script>
export let name;
</script>
<style>
h1 {
color: purple;
}
</style>
<h1>The dog name is {name}!</h1>
Будь-який JavaScript-код повинен бути між тегами script
:
Місце CSS — між тегами style
. Так зоною видимості стилів стає компонент, тому вони не «витікають» назовні. Тобто якщо інший компонент міститиме тег h1
, то ці стилі не впливатимуть на нього. Такий підхід дуже корисний для повторного використання компонентів, які ви вже створювали для інших застосунків, або якщо ви додаєте open-source бібліотеки.
Імпорт компонентів у інші компоненти
Як ми вже згадували, компонент може бути використаний іншими компонентами. Наприклад, певний компонент може імпортувати компонент Dog
у свій код.
Припустимо, що компонент House
розташований у файлі House.svelte
, у тій самій теці, що й Dog.svelte
.
<script>
import Dog from './Dog.svelte'
</script>
Тепер ви можете використовувати компонент Dog як HTML-тег:
<script>
import Dog from './Dog.svelte'
</script>
<Dog />
Експорт певних функцій з компонента
З прикладу вище ми бачимо, що для експорту компонента не треба робити зайвих кроків. Сам по собі компонент вже експортується за замовчуванням.
Як щодо експорту чогось окрім розмітки та функціоналу компонента?
Для цього треба додати до тегу script
атрибут context="module"
.
Розглянемо приклад. Припустимо, у вас є компонент Button у файлі Button.svelte
:
<button>A button</button>
Ви хочете, щоб інші компоненти могли впливати на колір кнопки.
Найкраще рішення для цього — використовувати props
(детальніше в наступному розділі).
Спочатку ми можемо визначити функцію changeColor
. Додамо їй певну логіку та експортуємо у тегу script
:
<script context="module">
export function changeColor() {
//...логіка зміни кольору..
}
</script>
<button>A button</button>
Зверніть увагу, що ви можете мати також «звичайний» тег script
в компоненті.
Тепер інші компоненти можуть імпортувати як Button, так і функцію changeColor
:
<script>
import Button, { changeColor } from './Button.svelte'
</script>
Обробка стану у Svelte
Кожен компонент (окрім розмітки, CSS та логіки JavaScript) може містити власний стан.
Що таке стан? Стан — дані, необхідні для рендерингу компонента. Наприклад, якщо поле вводу форми містить рядок «test», то десь визначена змінна, яка зберігає цей стан. Чекбокс чи радіокнопка вибрані? Потрібна змінна, щоб зафіксувати цей факт.
Стан зберігається в частині script
компонента:
<script>
let count = 0
</script>
Якщо у вас був досвід з Vue чи React, то вам, мабуть, цікаво, як оновити це значення. Svelte зручний тим, що вам не треба робити зайвих дій, аби оновити стан компонента.
Все що потрібно — присвоїти цей стан за допомогою оператора =
. Припустимо, є змінна count
. Ви можете збільшити її виразом count = count + 1
або count++
:
<script>
let count = 0
const incrementCount = () => {
count++
}
</script>
{count} <button on:click={incrementCount}>+1</button>
А в React, наприклад, вам довелося б викликати this.setState()
або використати хук useState()
.
У Vue більш структурований підхід з використанням класів та властивості data
.
У порівнянні з іншими фреймворками, Svelte більше нагадує JavaScript-стиль. Варто пам'ятати про одну річ: ми повинні також зробити присвоєння при зміні змінної. В іншому випадку Svelte не розпізнає, що стан змінився.
Для простих значень на зразок рядків чи чисел все вже задано, адже всі методи рядків повертають нові рядки, і те ж саме для чисел, тобто вони незмінні.
Як на рахунок масивів? Ми не можемо використовувати методи, які змінюють масив (на зразок push()
, pop()
, shift()
, splice()
), тому що вони не присвоюють нові значення, а змінюють внутрішню структуру даних, однак Svelte не може це розпізнати.
Тож усе-таки ви можете використовувати ці методи, однак потім вам треба перепризначити змінну в такий спосіб:
let list = [1, 2, 3]
list.push(4)
list = list
Здається, що все не дуже інтуїтивно, однак до цього можна швидко звикнути.
Інший варіант — використання оператора spread:
let list = [1, 2, 3]
list = [...list, 4]
Реактивність Svelte
У Svelte ви можете відстежувати зміни стану компонента та оновлювати інші змінні.
Наприклад, якщо у вас є змінна count
:
<script>
let count = 0
</script>
і ви оновлюєте її при кліку на кнопку:
<script>
let count = 0
const incrementCount = () => {
count = count + 1
}
</script>
{count} <button on:click={incrementCount}>+1</button>
Ви можете відстежувати зміни count
, використовуючи спеціальний синтаксис $:
. Так ви визначаєте новий блок коду, який Svelte виконає повторно, якщо будь-яка зазначена в ньому змінна модифікується:
Поглянемо на приклад:
<script>
let count = 0
const incrementCount = () => {
count = count + 1
}
$: console.log(`${count}`)
</script>
{count} <button on:click={incrementCount}>+1</button>
Ми використали блок:
$: console.log(`${count}`)
Можемо не обмежуватись одним блоком:
<script>
$: console.log(`the count is ${count}`)
$: console.log(`double the count is ${count * 2}`)
</script>
А також згрупувати декілька виразів в одному блоці:
<script>
$: {
console.log(`the count is ${count}`)
console.log(`double the count is ${count * 2}`)
}
</script>
Для прикладу ми взяли звичайний виклик console.log()
, однак ви можете оновити також інші змінні:
<script>
let count = 0
let double = 0
$: {
console.log(`the count is ${count}`)
double = count * 2
console.log(`double the count is ${double}`)
}
</script>
Props у Svelte
Ви можете імпортувати компонент Svelte в інший компонент, використовуючи синтаксис import ComponentName from 'componentPath'
:
<script>
import SignupForm from './SignupForm.svelte';
</script>
Шлях відносний щодо поточного компонента. Сполучення ./
означає «поточна тека». Якщо необхідно піднятись на одну теку вище — використовуйте таке позначення: ../
.
Тепер ви можете застосовувати щойно імпортований компонент в розмітці як HTML-тег:
<SignupForm />
Між компонентами формується відношення «батьківський-дочірній»: один імпортує, інший імпортований.
Часто необхідно, щоб батьківський компонент передавав дані дочірньому компоненту. Для цього можна використати props. Це щось подібне до атрибутів в чистому HTML для однонапрямної взаємодії між компонентами.
В наступному прикладі ми передаємо prop disabled
, визначивши для нього булеве значення true
:
<SignupForm disabled={true}/>
В компоненті SignupForm
вам необхідно експортувати prop в такий спосіб:
<script>
export let disabled
</script>
Так ми вказуємо, що prop передається батьківським компонентом.
При використанні компонента ви можете передати змінну замість значення, а тоді змінювати її динамічно:
<script>
import SignupForm from './SignupForm.svelte';
let disabled = true
</script>
<SignupForm disabled={disabled}/>
Коли значення disabled
змінюється, дочірній компонент оновиться відповідно до нового значення. Наприклад:
<script>
import SignupForm from './SignupForm.svelte';
let disabled = true
setTimeout(() => { disabled = false }, 2000)
</script>
<SignupForm disabled={disabled}/>
Управління станом між компонентами у Svelte
Ми вже розглянули, як легко Svelte оброляє стан одного компонента. А як щодо передачі стану між декількома компонентами?
Поширення стану за допомогою props
Це досить звичайна річ серед інших UI-фреймворків. Основний принцип — виносити стан наверх. Коли компоненту треба поділитися даними з іншими компонентами, стан можна винести до спільного для дочірніх батьківського компонента.
Стан передається деревом вниз, поки не знаходить ті компоненти, які потребують цієї інформації.
Описаний алгоритм працює за допомогою props, і найбільша перевага такого підходу — його простота.
Context API
Однак часом потрібен інший підхід. Наприклад, якщо два компоненти розташовані так далеко, що нам довелось би виносити стан у найвищий компонент в ієрархії.
В таких випадках можна скористатися іншою технікою — context API. Це ідеальний підхід, коли треба, щоб декілька компонентів обмінювались даними з дочірніми компонентами, але без використання props.
Context API забезпечується двома функціями з пакета svelte
: getContext
та setContext
:
Ви ініціалізуєте контекст певним об'єктом, визначивши для нього ключ:
<script>
import { setContext } from 'svelte'
const someObject = {}
setContext('someKey', someObject)
</script>
В іншому компоненті ви можете викликати getContext
, щоб отримати об'єкт за ключем:
<script>
import { getContext } from 'svelte'
const someObject = getContext('someKey')
</script>
Ви можете використовувати getContext
лише для того, щоб отримати ключ в компоненті, який встановлює контекст з setContext
або в його нащадках.
Якщо є компоненти з різних дерев і ви хочете, щоб вони обмінювались даними, використовуйте сховища.
Використання сховищ (stores) Svelte
Сховища Svelte — чудовий інструмент для обробки стану застосунку, коли не бажано, щоб компоненти часто обмінювались props.
Спочатку потрібно імпортувати writable
з svelte/store
:
import { writable } from 'svelte/store'
та створити змінну сховища за допомогою функції writable()
, передавши значення за замовчуванням як перший аргумент:
const username = writable('Guest')
Ви можете винести частину коду зі сховищем в окремий файл (наприклад store.js
, .js
, тому що це компонент Svelte) та імпортувати його в декілька компонентів.
import { writable } from 'svelte/store'
export const username = writable('Guest')
Будь-який інший компонент, який завантажить цей файл тепер зможе отримати значення зі сховища:
<script>
import { username } from './store.js'
</script>
Значення імпортованої змінної можна змінити за допомогою set()
:
username.set('new username')
А оновити за допомогою функції update()
. Вона відрізняється від set()
тим, що ви передаєте не просто нове значення, а колбек-функцію з поточним значенням як аргумент:
const newUsername = 'new username!'
username.update(existing => newUsername)
Можемо додати більше логіки:
username.update(existing => {
console.log(`Updating username from ${existing} to ${newUsername}`)
return newUsername
})
Щоб одноразово отримати значення зі сховища, ви можете експортувати функцію get()
з svelte/store
.
import { writable, get } from 'svelte/store'
export const username = writable('Guest')
get(username) //'Guest'
Щоб створити реактивну змінну, яка оновлюватиметься при модифікації змінної сховища, ви можете додати на початок назви змінної символ $
(в нашому прикладі це $username
). Ваш компонент буде повторно рендеритись при змінах у сховищі.
Зверніть увагу: Svelte розглядає символ $
як зарезервоване значення, та не дозволить використовувати його для змінних, що не стосуються сховища (а це може збивати з пантелику). Тож якщо ви звикли називати змінні, що посилаються на DOM з $
, не робіть цього у Svelte.
Існує й інший варіант, якщо вам треба виконати певну логіку при зміні значення змінної — метод subscribe()
.
username.subscribe(newValue => {
console.log(newValue)
})
Окрім сховищ зі змінюваними даними, Svelte пропонує також два додаткових види сховищ: сховища читання та сховища наслідування.
Сховища читання у Svelte
Сховище читання відрізняється тим, що його не можна оновити зовні — там немає методів set()
та update()
. Після того як ви визначили початкове значення, змінити його вже не можна.
Офіційна документація Svelte показує цікавий приклад таймера. Окрім встановлення таймера, можна також отримувати певні ресурси з мережі, робити виклики API, отримувати дані файлової системи (використовуючи локальний Node.js сервер) тощо.
В нашому випадку ми замість ініціалізації змінної сховища з writable()
використаємо readable()
:
import { readable } from 'svelte/store'
export const count = readable(0)
Ви також можете передати функцію, відповідальну за оновлення значення. Функція отримує метод set()
для модифікації значення:
<script>
import { readable } from 'svelte/store'
export const count = readable(0, set => {
setTimeout(() => {
set(1)
}, 1000)
})
</script>
Тут ми оновлюємо значення після того, як пройде секунда. Можна виконувати код через певний інтервал:
import { readable, get } from 'svelte/store'
export const count = readable(0, set => {
setInterval(() => {
set(get(count) + 1)
}, 1000)
})
Таку логіку можна використовувати в іншому компоненті, імпортувавши функцію:
<script>
import { count } from './store.js'
</script>
{$count}
Сховища наслідування у Svelte
Сховища наслідування дозволяють додати нове значення до сховища, яке базується на значенні наявного сховища.
Для цього можна використати функцію derived()
, експортовану зі svelte/store
, яка приймає першим параметром наявне значення в сховищі, а як другий параметр — функцію, що використовує це значення:
import { writable, derived } from 'svelte/store'
export const username = writable('Guest')
export const welcomeMessage = derived(username, $username => {
return `Welcome ${$username}`
})
<script>
import { username, welcomeMessage } from './store.js'
</script>
{$username}
{$welcomeMessage}
Слоти у Svelte
Слоти — зручний спосіб об'єднання компонентів, а ще вони корисні для налаштування імпортованих компонентів.
Пояснимо, як все працює. В компоненті ви можете визначити слот, вказавши <slot />
чи <slot></slot>
.
Розглянемо компонент Button.svelte
, який містить розмітку для кнопки <button>
. В React це було б так: <button>{props.children}</button>
.
Тепер в іншому компоненті ми можемо передати дочірні компоненти для нашого Button
і вони будуть поміщені в слот.
<script>
import Button from './Button.svelte'
</script>
<Button>Insert this into the slot</Button>
Ви можете визначити для слота значення за замовчуванням, якщо не буде передбачено іншого:
<button>
<slot>
Default text for the button
</slot>
</button>
В компоненті може бути декілька слотів, ви можете відрізняти їх між собою за допомогою атрибута name
. Слот без назви буде використовуватись за замовчуванням:
<slot name="before" />
<button>
<slot />
</button>
<slot name="after" />
А застосовувати це можна так:
<script>
import Button from './Button.svelte'
</script>
<Button>
Insert this into the slot
<p slot="before">Add this before</p>
<p slot="after">Add this after</p>
</Button>
А так це все буде у DOM:
<p slot="before">Add this before</p>
<button>
Insert this into the slot
</button>
<p slot="after">Add this after</p>
Події життєвого циклу компонента Svelte
Кожен компонент у Svelte має декілька етапів життєвого циклу, на кожному з них ми можемо реалізовувати певний функціонал.
Існують такі події життєвого циклу:
onMount
— запускається після рендерингу компонента;onDestroy
— запускається після знищення компонента;beforeUpdate
— запускається перед оновленням DOM;afterUpdate
— запускається після оновлення DOM.
Ми можемо запланувати функції, які запускатимуться Svelte на певних етапах.
За замовчуванням ми не маємо доступу до перелічених методів, однак нам треба імпортувати їх з пакета svelte
:
<script>
import { onMount, onDestroy, beforeUpdate, afterUpdate } from 'svelte'
</script>
Поширений варіант використання onMount
— отримання даних зі стороннього ресурсу.
Приклад використання onMount
:
<script>
import { onMount } from 'svelte'
onMount(async () => {
// певні дії
})
</script>
onDestroy
дозволяє очистити дані або зупинити операцію, яку ми запустили при ініціалізації компонента (на зразок таймерів або setInterval
).
Якщо ви повертаєте функцію з onMount
, вона виконуватиметься на тому ж етапі, що й onDestroy
:
<script>
import { onMount } from 'svelte'
onMount(async () => {
//ініціалізація компонента
return () => {
// після знищення компонента
}
})
</script>
Розглянемо практичний приклад, як запускати періодичну функцію при монтуванні та очищати інтервал після знищення компонента:
<script>
import { onMount } from 'svelte'
onMount(async () => {
const interval = setInterval(() => {
console.log('hey, just checking!')
}, 1000)
return () => {
clearInterval(interval)
}
})
</script>
Зв'язування у Svelte
Зі Svelte ви можете створити двонапрямне зв'язування між даними та UI. Це досить поширений патерн серед сучасних веб-фреймворків, особливо корисний для форм:
bind:value
Почнемо з найбільш поширеної форми зв'язування, використовуючи конструкцію bind:value
. Ви берете змінну зі стану компонента та прив'язуєте її до поля:
<script>
let name = ''
</script>
<input bind:value={name}>
Тепер якщо name
зміниться, значення поля вводу зміниться відповідно. І навпаки: якщо користувач вводить певне значення в поле: змінна name
оновлюється.
Пам'ятайте, що змінну потрібно оголошувати ключовим словом let/var
, а не const
, оскільки в останньому випадку Svelte не зможе оновити значення змінної.
bind:value
працює для різних типів полів вводу (type="number"
, type="email"
тощо), але також і для інших типів на зразок textarea
та select
(більше про select
далі).
Чекбокси та радіокнопки
Чекбокси та радіокнопки (input
-елементи з атрибутами type="checkbox"
чи type="radio"
) дозволяють три види зв'язування:
bind:checked
bind:group
bind:indeterminate
bind:checked
дозволяє прив'язати значення до властивості checked
елемента:
<script>
let isChecked
</script>
<input type=checkbox bind:checked={isChecked}>
bind:group
корисний для чекбоксів та радіокнопок, оскільки вони часто використовуються в групах. З bind:group
ви можете поставити у відповідність масиву JavaScript список чекбоксів та наповнити його залежно від вибору користувача.
Розглянемо приклад. Масив goodDogs
наповнюється обраними елементами:
<script>
let goodDogs = []
let dogs = ['Roger', 'Syd']
</script>
<h2>
Who's a good dog?
</h2>
<ul>
{#each dogs as dog}
<li>{dog} <input type=checkbox bind:group={goodDogs} value={dog}></li>
{/each}
</ul>
<h2>
Good dogs according to me:
</h2>
<ul>
{#each goodDogs as dog}
<li>{dog}</li>
{/each}
</ul>
Приклад можна оглянути наживо за посиланням.
bind:indeterminate
дозволяє нам прив'язатись до indeterminate
стану елемента (більше — за посиланням)
Select
bind:value
також працює для поля типу select
, щоб автоматично призначати обране значення змінній:
<script>
let selected
</script>
<select bind:value={selected}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
{selected}
Якщо ви генеруєте варіанти динамічно з масиву чи об'єктів, обраний варіант буде об'єктом, а не рядком:
<script>
let selected
const goodDogs = [
{ name: 'Roger' },
{ name: 'Syd' }
]
</script>
<h2>List of possible good dogs:</h2>
<select bind:value={selected}>
{#each goodDogs as goodDog}
<option value={goodDog}>{goodDog.name}</option>
{/each}
</select>
{#if selected}
<h2>
Good dog selected: {selected.name}
</h2>
{/if}
Перевірте за посиланням.
select
також дозволяє використовувати атрибут multiple
:
<script>
let selected = []
const goodDogs = [
{ name: 'Roger' },
{ name: 'Syd' }
]
</script>
<h2>List of possible good dogs:</h2>
<select multiple bind:value={selected}>
{#each goodDogs as goodDog}
<option value={goodDog}>{goodDog.name}</option>
{/each}
</select>
{#if selected.length}
<h2>Good dog selected:</h2>
<ul>
{#each selected as dog}
<li>{dog.name}</li>
{/each}
</ul>
{/if}
Приклад за посиланням.
Інші види зв'язування
Види зв'язувань залежать від HTML-тегів, з якими ви працюєте.
bind:files
працює для полів з атрибутом type="file"
для зв'язування списку обраних файлів.
HTML-елемент details
дозволяє використання bind:open
для зв'язування значень відкритого і закритого стану.
Медіатеги audio
та video
в HTML також дозволяють зв'язувати деякі властивості: currentTime
, duration
, paused
, buffered
, seekable
, played
, volume
, playbackRate
.
textContent
та innerHTML
можна прив'язати до contenteditable
полів.
Read-only зв'язування
offsetWidth
, offsetHeight
, clientWidth
, clientHeight
зв'язування можуть використовуватись лише для читання для будь-якого блокового HTML-елемента за винятком пустих тегів (на зразок br
) та елементів, які явно встановлені як рядкові за допомогою display: inline
.
Отримання посилання на HTML-елемент в JavaScript
bind:this
— особливий вид зв'язування, який дозволяє отримати посилання на HTML-елемент та прив'язати його до JavaScript-змінної:
<script>
let myInputField
</script>
<input bind:this={myInputField} />
Це корисно, коли вам треба виконати деяку логіку над елементом, наприклад, використовуючи колбек onMount()
.
Зв'язування props компонента
З конструкцією bind:
ви можете прив'язати значення до будь-якого prop-компонента.
Припустимо, у нас є компонент Car.svelte
:
<script>
export let inMovement = false
</script>
<button on:click={() => inMovement = true }>Start car</button>
Можна імпортувати компонент та прив'язати prop inMovement
:
<script>
import Car from './Car.svelte';
let carInMovement;
</script>
<Car bind:inMovement={carInMovement} />
{carInMovement}
Умовні вирази в шаблонах
В компоненті Svelte при рендерингу HTML можна використати особливий синтаксис, щоб зробити UI досконалим на кожному етапі життєвого циклу застосунку.
Розглянемо умовні структури. Припустимо, вам необхідно виконати різні дії, залежно від умови. Svelte пропонує дуже потужний набір умовних структур.
Перша з них – if
:
{#if isRed}
<p>Red</p>
{/if}
Вираз стоїть між конструкцією {#if}
та {/if}
. В конструкції ми перевіряємо значення змінної чи виразу. Якщо змінна isRed
приймає значення true
, буде відповідна розмітка:
<script>
let isRed = true
</script>
Варто пам'ятати, що пустий рядок, 0 та false
належать до хибних значень, а рядок з вмістом, число > 0 чи true
— правдиві значення.
Якщо в умовний вираз потрапляє хибне значення, ми не побачимо розмітку.
Ми можемо використати конструкцію else
, якщо треба показати розмітку при хибному значенні:
{#if isRed}
<p>Red</p>
{:else}
<p>Not red</p>
{/if}
Залежно від значення isRed
ми побачимо одну з розміток.
Ви можете використовувати будь-який JavaScript-вираз у блоці if
, а для заперечення — символ !
:
{#if !isRed}
<p>Not red</p>
{:else}
<p>Red</p>
{/if}
Всередині else
ви можете перевірити додаткову умову. Тут на допомогу приходить вираз {:else if somethingElse}
:
{#if isRed}
<p>Red</p>
{:else if isGreen}
<p>Green</p>
{:else}
<p>Not red nor green</p>
{/if}
Ви можете мати більше ніж один подібний блок, допускається і вкладеність. Ось складніший приклад:
{#if isRed}
<p>Red</p>
{:else if isGreen}
<p>Green</p>
{:else if isBlue}
<p>It is blue</p>
{:else}
{#if isDog}
<p>It is a dog</p>
{/if}
{/if}
Циклічні конструкції в шаблонах Svelte
В шаблонах Svelte ви можете використовувати цикли з конструкціями {#each}{/each}
:
<script>
let goodDogs = ['Roger', 'Syd']
</script>
{#each goodDogs as goodDog}
<li>{goodDog}</li>
{/each}
Якщо ви мали досвід з іншими фреймворками, що використовують шаблони, синтаксис дуже схожий.
Щоб отримати індекс кожної ітерації:
<script>
let goodDogs = ['Roger', 'Syd']
</script>
{#each goodDogs as goodDog, index}
<li>{index}: {goodDog}</li>
{/each}
При динамічному редагуванні списку, видаленні та додаванні елементів, необхідно завжди передавати ідентифікатор .
Розглянемо приклад:
<script>
let goodDogs = ['Roger', 'Syd']
</script>
{#each goodDogs as goodDog (goodDog)}
<li>{goodDog}</li>
{/each}
<!-- з індексом -->
{#each goodDogs as goodDog, index (goodDog)}
<li>{goodDog}</li>
{/each}
Ви також можете передати об'єкт, але якщо ваш список має унікальний ідентифікатор для кожного елемента, краще використовувати його:
<script>
let goodDogs = [
{ id: 1, name: 'Roger'},
{ id: 2, name: 'Syd'}
]
</script>
{#each goodDogs as goodDog (goodDog.id)}
<li>{goodDog.name}</li>
{/each}
<!-- з використанням index -->
{#each goodDogs as goodDog, index (goodDog.id)}
<li>{goodDog.name}</li>
{/each}
Проміси в шаблонах Svelte
Проміси — чудовий інструмент для роботи з асинхронними подіями в JavaScript. А з введенням в ES2017 синтаксису await
, використовувати проміси стало ще легше.
Svelte пропонує конструкцію {#await}
в шаблонах для прямої роботи з промісами на рівні шаблону.
Ми можемо почекати, коли проміс перейде в статус resolved
, та визначити інший UI для різних станів промісу: unresolved
, resolved
та rejected
.
Розглянемо, як усе працює. Ми оголошуємо проміс та використовуємо блок {#await}
, де ми чекаємо на його результат.
Як тільки проміс перейде в стан resolved
, результат передається в блок {:then}
:
<script>
const fetchImage = (async () => {
const response = await fetch('https://web.archive.org/web/20230325134733/https://dog.ceo/api/breeds/image/random')
return await response.json()
})()
</script>
{#await fetchImage}
<p>...waiting</p>
{:then data}
<img src={data.message} alt="Dog image" />
{/await}
Ви можете відловити стан rejected
проміса у {:catch}
блоці:
{#await fetchImage}
<p>...waiting</p>
{:then data}
<img src={data.message} alt="Dog image" />
{:catch error}
<p>An error occurred!</p>
{/await}
З прикладом можна ознайомитись за посиланням.
Робота з подіями у Svelte
Відстеження DOM-подій
У Svelte ви можете визначити слухача для подій DOM одразу в шаблоні, через синтаксис on:<event>
.
Наприклад, щоб відстежити подію click
, необхідно передати функцію в атрибут on:click
. Щоб відстежити подію onmousemove
, ми передаємо функцію в атрибут on:mousemove
відповідно.
Розглянемо приклад з функцією-обробником, визначеною в атрибуті:
<button on:click={() => {
alert('clicked')
}}>Click me</button>
Та інший приклад з функцією-обробником в секції script
компонента:
<script>
const doSomething = () => {
alert('clicked')
}
</script>
<button on:click={doSomething}>Click me</button>
Вбудована функція більше підходить для випадків, коли в обробнику мало коду. В іншому випадку краще звернутись до <script>
.
Svelte передає обробник події як аргумент функції, а це зручно, щоб зупинити виплиття (propagation), або щоб створити посилання на властивості об'єкта Event
:
<script>
const doSomething = event => {
console.log(event)
alert('clicked')
}
</script>
Вище ми згадали про «припинення виплиття». Це часто потрібно, наприклад, щоб зупинити відправлення форми. Svelte пропонує модифікатори, щоб не робити це вручну. stopPropagation
та preventDefault
застосовуються найчастіше.
Для модифікаторів існує такий синтаксис: <button on:click|stopPropagation|preventDefault={doSomething}>Click me</button>
.
Бувають й інші модифікатори, однак вони використовуються рідше. capture
зумовлює події захвату, замість спливання
, once
запускає подію одноразово, self
запускає подію лише якщо цільовий елемент події — поточний об'єкт (видалення його з ієрархії захвату/виплиття).
Створення власних подій в компонентах
Ми можемо створювати також власні події в компонентах та використовувати такий самий синтаксис, як і у вбудованих DOM-подіях. Для цього нам треба імпортувати функцію createEventDispatcher
з пакета svelte
та викликати її, щоб створити диспетчер подій:
<script>
import { createEventDispatcher } from 'svelte'
const dispatch = createEventDispatcher()
</script>
Після цього ми можемо викликати dispatch()
, передати рядок, який ідентифікує подію (використовуватимемо його в конструкції on:
в компонентах):
<script>
import { createEventDispatcher } from 'svelte'
const dispatch = createEventDispatcher()
//коли час запустити подію
dispatch('eventName')
</script>
Тепер інші компоненти можуть використовувати наші рядки:
<ComponentName on:eventName={event => {
//певна логіка
}} />
Ми також можемо передати об'єкт як другий параметр dispatch()
:
<script>
import { createEventDispatcher } from 'svelte'
const dispatch = createEventDispatcher()
const value = 'something'
//коли час виконати подію
dispatch('eventName', value)
//або
dispatch('eventName', {
someProperty: value
})
</script>
Об'єкт, переданий в dispatch()
, буде доступним в event
.
Що далі?
Якщо вас зацікавив фреймворк і ви б хотіли б дізнатись більше, ознаймтесь з:
- Офіційним веб-сайтом Svelte;
- Sapper — фреймворком, створеним на базі Svelte для SSR-застосунків на Node.js та Svelte.
Ще немає коментарів