Важное замечание

Этот документ находится в разработке. Пожалуйста, отнеситесь к нам со cнисхождением за все недостающие или вводящие в заблуждение детали, и не стесняйтесь просить о помощи в чате Discord. Учебник сейчас является наиболее актуальным и полным, так что лучше начинать с него.

Этот раздел содержит подробную справочную документацию по API. Он предназначен для людей, которые уже знакомы с Svelte.

Если вы ещё не успели познакомиться с нашим фреймворком, сделайте это в интерактивном Учебнике или посмотрите примеры — тогда эта документация может оказаться более полезной для вас.

Формат компонента

Компоненты — это строительные кирпичики любого приложения на фреймворке Svelte. Они описываются в файлах с расширением .svelte при помощи надмножества языка разметки HTML.

Все три части — script, style и разметка — не являются обязательными.

<script>
	// логика описывается здесь
</script>

<style>
	/* стили должны быть здесь */
</style>

<!-- разметка (0 или более элементов) помещается здесь -->

<script>

Блок <script> содержит JavaScript, который запускается при создании экземпляра компонента. Переменные, объявленные (или импортированные) в этом блоке, 'видны' из разметки компонента. Есть ещё четыре дополнительных правила:

1. export объявляет свойство компонента

Svelte использует ключевое слово export, чтобы пометить объявление переменной как свойство, что означает, что оно становится доступным извне всему, что будет использовать этот компонент:

<script>
	// эти свойства могут быть заданы извне
	export let foo;
	export let bar = 'опциональное значение по умолчанию';

	// можно использовать export { ... as ... } для
	// создания свойства с именем, которое является конструкцией языка
	let clazz;
	export { clazz as class };

	// Значения переменных, объявленных как свойства,
	// доступны сразу же
	console.log(foo, bar);

	// Функции также могут быть свойствами
	export let format = (number) => (number.toFixed(2));

	// Объявления функций становятся методами компонента,
	// а не его свойствами.
	export function greetMethod() {
		alert(`I'm a <${this.constructor.name}>!`);

	// также используйте export { ... as ... } для
	// создания метода с именем, которое является конструкцией языка
	function del() {
		do_something();
	}
	export { del as delete };
</script>
2. Присваивания 'реактивны'

Чтобы изменить состояние компонента и запустить его перерисовку, просто присвойте что-либо переменной, объявленной локально.

Присваивания с обновлением (count += 1) и присваивания свойствам (obj.x = y) будут иметь тот же эффект.

Поскольку реактивность Svelte основана на присваиваниях, использование таких методов массива, как .push() и .splice(), не приведёт к автоматическому обновлению. Но вы можете узнать в учебнике, как обойти это ограничение.

<script>
	let count = 0;

	function handleClick () {
		// вызов этой функции приведёт к перерисовке компонента,
		// если в разметке есть ссылка на `count`
		count = count + 1;
	}
</script>
3. $: делает выражение реактивным

Любое выражение на верхнем уровне (то есть, не внутри блока или функции) можно сделать реактивным, добавив перед ним JS метку $:. Реактивные выражения запускаются непосредственно перед обновлением компонента всякий раз, когда изменяются значения переменных, которые в него входят.

<script>
	export let title;

	// это выражение будет обновлять `document.title` каждый раз,
	// когда свойство `title` изменится
	$: document.title = title;

	$: {
		console.log(`можно объединить в блок несколько выражений`);
		console.log(`текущий заголовок: ${title}`);
	}
</script>

Если выражением является только присваивание значения ранее не объявленной переменной, Svelte самостоятельно объявит такую переменную через оператор let.

<script>
	export let num;

	// нет необходимости объявлять `squared` и `cubed`,
	// Svelte сделает это за нас
	$: squared = num * num;
	$: cubed = squared * num;
</script>
4. Добавьте префикс $ к хранилищу для получения его значения

Каждый раз, когда вам нужно взять значение из хранилища, вы можете вы можете сделать это, поместив перед ним префикс с символом $. Это говорит Svelte, что нужно объявить переменную с префиксом и подписаться на хранилище с автоматическим удалением подписки при необходимости.

Обратите внимание, что хранилище должно быть объявлено на верхнем уровне компонента, а не, например, внутри функции или блока if.

Локальные переменные (которые не являются ссылкой на хранилище) не должны иметь префикс $.

<script>
	import { writable } from 'svelte/store';

	const count = writable(0);
	console.log($count); // выведет 0

	count.set(1);
	console.log($count); // выведет 1
</script>

<script context="module">

Блок <script> с атрибутом context="module" выполняется только один раз при первичной обработке модуля, а не при каждой инициализации экземпляров компонента. Значения, объявленные в этом блоке, доступны в разметке компонента и в обычном блоке <script>(но не наоборот).

Все, что экспортируется из такого блока с помощью оператора export, становится экспортом из самого скомпилированного модуля.

Вы не сможете сделать export default, потому что сам компонент уже является экспортом по умолчанию.

<script context="module">
	let totalComponents = 0;

	// такая запись позволит импортировать в нужном месте эту функцию:
	// `import Example, { alertTotal } from './Example.svelte'`
	export function alertTotal() {
		alert(totalComponents);
	}
</script>

<script>
	totalComponents += 1;
	console.log(`для этого компонента было создано ${totalComponents} экземпляр(ов)`);
</script>

<style>

CSS стили внутри блока <style> будут изолированы внутри данного компонента.

Это достигается путём добавления класса ко всем затронутым элементам, имя которого получено хэш-функцией из стилей компонента (например, svelte-123xyz).

<style>
	p {
		/* это затронет только элемент <p> в этом компоненте */
		color: burlywood;
	}
</style>

Для применения стиля к селектору глобально, используйте модификатор :global(...).

<style>
	:global(body) {
		/* этот стиль для <body> */
		margin: 0;
	}

	div :global(strong) {
		/* этот стиль будет применяться ко всем элементам <strong> 
		   в любом компоненте, который находится внутри 
		   элементов <div> данного компонента */
		color: goldenrod;
	}
</style>

Синтаксис шаблонов

Теги

Тег в нижнем регистре, например <div> — это обычный HTML элемент. Тег, написанный с большой буквы, такой как <Widget> или <Namespace.Widget>, обозначает компонент.

<script>
	import Widget from './Widget.svelte';
</script>

<div>
	<Widget/>
</div>

Атрибуты

По умолчанию атрибуты работают точно так же, как их HTML-аналоги.

<div class="foo">
	<button disabled>ненажимаемая кнопка</button>
</div>

Как и в HTML, значения могут быть указаны без кавычек.

<input type=checkbox>

Значения атрибута могут содержать выражения JavaScript.

<a href="page/{p}">страница {p}</a>

Или они целиком могут быть выражениями JavaScript.

<button disabled={!clickable}>...</button>

Выражение может содержать символы, которые могут вызвать проблемы при подсветке синтаксиса в обычном HTML, поэтому допускается использование значения в кавычках. Кавычки не влияют на то, как анализируется значение:

<button disabled="{number !== 42}">...</button>

Когда имя и значение атрибута совпадают (name = {name}) — их можно заменить на {name}.

<!-- Эти строки эквивалентны -->
<button disabled={disabled}>...</button>
<button {disabled}>...</button>

Развёртка атрибутов позволяет передать компоненту сразу несколько атрибутов или свойств.

Элемент или компонент может иметь сразу несколько таких развёрток вперемешку с обычными атрибутами.

<Widget {...things}/>

Текстовые выражения

{выражение}

Текст также может содержать JavaScript-выражения:

<h1>Привет, {name}!</h1>
<p>{a} + {b} = {a + b}.</p>

{#if ...}

{#if выражение}...{/if}
{#if выражение}...{:else if выражение}...{/if}
{#if выражение}...{:else}...{/if}

Для отображения содержимого при выполнении какого-либо условия, нужно добавить его в блок if.

{#if answer === 42}
	<p>а какой был вопрос?</p>
{/if}

Дополнительные условия могут быть добавлены с помощью {:else if выражение}, а блок альтернативной разметки помещен после {:else}.

{#if porridge.temperature > 35}
	<p>слишком горячая!</p>
{:else if 25 > porridge.temperature}
	<p>слишком холодная!</p>
{:else}
	<p>то, что надо!</p>
{/if}

{#each ...}

{#each выражение as имя}...{/each}
{#each выражение as имя, индекс}...{/each}
{#each выражение as имя, индекс (ключ)}...{/each}
{#each выражение as имя}...{:else}...{/each}

Перебор списков значений может быть выполнен блоком each.

<h1>Список покупок</h1>
<ul>
	{#each items as item}
		<li>{item.name} x {item.qty}</li>
	{/each}
</ul>

Блок each также может отдавать индекс элемента, аналогично второму аргументу callback-функции в array.map(...):

{#each items as item, i}
	<li>{i + 1}: {item.name} x {item.qty}</li>
{/each}

Если указать параметр ключ, который однозначно идентифицирует каждый элемент списка, то при изменении данных Svelte будет использовать его для изменения списка в нужном месте, а не просто удалять и добавлять элементы в конец массива. Ключом может быть любой объект, но рекомендуется использовать строки и числа, поскольку они позволяют сохранять уникальность при изменении самих объектов.

{#each items as item, i (item.id)}
	<li>{i + 1}: {item.name} x {item.qty}</li>
{/each}

При желании в блоках each можно использовать деструктуризацию и развёртку:

{#each items as { id, name, qty }, i (id)}
	<li>{i + 1}: {name} x {qty}</li>
{/each}

{#each objects as { id, ...rest }}
	<li><span>{id}</span><MyComponent {...rest}/></li>
{/each}

{#each items as [id, ...rest]}
	<li><span>{id}</span><MyComponent values={rest}/></li>
{/each}

Блок each тоже может иметь блок {:else}, который будет отрисовываться, если переданный список окажется пустым.

{#each todos as todo}
	<p>{todo.text}</p>
{:else}
	<p>Нет задач!</p>
{/each}

{#await ...}

{#await выражение}...{:then имя}...{:catch имя}...{/await}
{#await выражение}...{:then имя}...{/await}
{#await выражение then имя}...{/await}

Блоки await позволяют обрабатывать три возможных состояния промисов — ожидание, выполнение или отклонение.

{#await promise}
	<!-- промис в состоянии ожидания -->
	<p>Ждём пока промис выполнится...</p>
{:then value}
	<!-- промис выполнился -->
	<p>Значение равно {value}</p>
{:catch error}
	<!-- промис отклонён -->
	<p>Что-то пошло не так: {error.message}</p>
{/await}

Блок catch может быть опущен, если не нужно ничего отрисовывать, при отклонении промиса (или это вообще невозможно).

{#await promise}
	<!-- промис в состоянии ожидания -->
	<p>Ждём пока промис выполнится...</p>
{:then value}
	<!-- промис выполнился -->
	<p>Значение равно {value}</p>
{/await}

Если состояние ожидания промиса не требует информирования, можно также опустить и первый блок.

{#await promise then value}
	<p>Значение равно {value}</p>
{/await}

{@html ...}

{@html выражение}

В текстовых выражениях, символы вроде < и > экранируются. В HTML выражениях — нет.

Svelte не очищает HTML код перед его обработкой! Если данные приходят из ненадёжного источника, необходимо их проверить. В противном случае, вы подвергаете пользователей возможным XSS-атакам.

<div class="blog-post">
	<h1>{post.title}</h1>
	{@html post.content}
</div>

{@debug ...}

{@debug}
{@debug переменная1, переменная2, ..., переменнаяN}

Тег {@debug ...} — это альтернатива для функции console.log (...). Он отображает значение указанных переменных при их изменении, и приостанавливает дальнейшее выполнение кода при открытых инструментах разработчика в браузере.

Он принимает разделенный запятыми список имён переменных (не любых выражений).

<script>
	let user = {
		firstname: 'Ада',
		lastname: 'Лавлейс'
	};
</script>

{@debug user}

<h1>Привет {user.firstname}!</h1>

{@debug ...} принимает разделенный запятыми список имён переменных (не любых выражений).

<!-- Успешно скомпилируется -->
{@debug user}
{@debug user1, user2, user3}
<!-- НЕ скомпилируется -->
{@debug user.firstname}
{@debug myArray[0]}
{@debug !isReady}
{@debug typeof user === 'object'}

Тег {@debug} без каких-либо аргументов установит оператор debugger, который будет срабатывать при любом изменении состояния.

Директивы элементов

Наряду с атрибутами у элементов могут быть и директивы, которые предоставляют некоторые дополнительные возможности.

on:событие

on:событие={обработчик}
on:событие|модификаторы={обработчик}

Используйте директиву on: для обработки событий в DOM.

<script>
	let count = 0;

	function handleClick(event) {
		count += 1;
	}
</script>

<button on:click={handleClick}>
	счётчик: {count}
</button>

Можно задать обработчики непосредственно на месте, без какой-либо потери производительности. Как и у атрибутов, значения директив могут быть заключены в кавычки для совместимости с подсветкой синтаксиса.

<button on:click="{() => count += 1}">
	счётчик: {count}
</button>

Модификаторы событий DOM добавляются после символа |.

<form on:submit|preventDefault={handleSubmit}>
	<!-- стандартная обработка события `submit` предотвращена,
		 поэтому страница не перезагрузится -->
</form>

Доступны следующие модификаторы:

  • preventDefault — вызывает event.preventDefault() перед запуском обработчика. Полезно, в том числе для обработки форм на клиентской стороне.
  • stopPropagation — вызывает event.stopPropagation(), предотвращает распространение события до следующих элементов.
  • passive — улучшает производительность прокрутки при тач-событиях или при прокрутке колёсиком мышки (Svelte добавит этот модификатор автоматически там, где это безопасно).
  • capture — вызывает событие в режиме capture вместо bubbling.
  • once — удаляет обработчик события после первого вызова.

Модификаторы можно соединять в цепочку, например on:click|once|capture={...}.

Если директива on: используется без значения, то компонент будет пробрасывать событие. Таким образом можно обрабатывать события из глубоко вложенных компонентов.

<button on:click>
	Так событие клика по кнопке сможет выйти за пределы компонента
</button>

Можно назначить несколько обработчиков для одного события:

<script>
	let counter = 0;
	function increment() {
		counter = counter + 1;
	}
	function track(event) {
		trackEvent(event)
	}
</script>

<button on:click={increment} on:click={track}>Нажми меня!</button>

bind:свойство

bind:свойство={переменная}

Данные обычно передаются от родительского элемента к потомкам. Директива bind: позволяет передавать данные в другую сторону, от дочернего элемента в родительский. Большинство привязок соотносятся с конкретными элементами.

Простейшие привязки просто передают значение свойства элемента, например, input.value.

<input bind:value={name}>
<textarea bind:value={text}></textarea>

<input type="checkbox" bind:checked={yes}>

Если имя свойства и значения одинаковые, можно использовать сокращение.

<!-- Эти строки эквивалентны -->
<input bind:value={value}>
<input bind:value>

Значения из числовых input элементов автоматически приводятся к нужному типу. В структуре DOM значение свойства input.value таких элементов будет являться строкой, но Svelte будет рассматривать его как число. Если значение input пустое или недействительное (в случае type="number"), оно будет равно undefined.

<input type="number" bind:value={num}>
<input type="range" bind:value={num}>
Привязка к значению <select>

Привязка значения <select> соответствует свойству value в выбранном <option>, которое может быть абсолютно любым значением, а не только строкой, как это обычно бывает в DOM.

<select bind:value={selected}>
	<option value={a}>a</option>
	<option value={b}>b</option>
	<option value={c}>c</option>
</select>

Элемент <select multiple> ведет себя аналогично группе чекбоксов.

<select multiple bind:value={fillings}>
	<option value="Rice">Rice</option>
	<option value="Beans">Beans</option>
	<option value="Cheese">Cheese</option>
	<option value="Guac (extra)">Guac (extra)</option>
</select>

Когда значение <option> соответствует его текстовому содержимому, атрибут может быть опущен.

<select multiple bind:value={fillings}>
	<option>Rice</option>
	<option>Beans</option>
	<option>Cheese</option>
	<option>Guac (extra)</option>
</select>
Привязка к медиа-элементам

Медиа-элементы (<audio> и <video>) имеют свой собственный набор привязок — четыре только для чтения ...

  • duration (только для чтения) — общая продолжительность, в секундах
  • buffered (только для чтения) — буфер, массив объектов {start, end}
  • seekable (только для чтения) — доступное для перемотки, то же самое
  • played (только для чтения) — уже проиграно, то же самое

... и три двусторонние привязки:

  • currentTime — текущая позиция проигрывания, в секундах
  • paused — остановлено проигрывание или нет
  • volume — громкость, значение между 0 и 1
<video
	src={clip}
	bind:duration
	bind:buffered
	bind:seekable
	bind:played
	bind:currentTime
	bind:paused
	bind:volume
></video>
Привязка к блочным элементам

У блочных элементов есть 4 привязки, доступных только для чтения. Они рассчитываются с использованием метода, аналогичного этому:

  • clientWidth
  • clientHeight
  • offsetWidth
  • offsetHeight
<div
	bind:offsetWidth={width}
	bind:offsetHeight={height}
>
	<Chart {width} {height}/>
</div>

bind:group

bind:group={переменная}

Элементы input, которые работают вместе, могут использовать привязку bind:group.

<script>
	let tortilla = 'Plain';
	let fillings = [];
</script>

<!-- сгруппированные радиокнопки являются взаимоисключающими -->
<input type="radio" bind:group={tortilla} value="Plain">
<input type="radio" bind:group={tortilla} value="Whole wheat">
<input type="radio" bind:group={tortilla} value="Spinach">

<!-- сгруппированные чекбоксы образуют массив своих значений -->
<input type="checkbox" bind:group={fillings} value="Rice">
<input type="checkbox" bind:group={fillings} value="Beans">
<input type="checkbox" bind:group={fillings} value="Cheese">
<input type="checkbox" bind:group={fillings} value="Guac (extra)">

bind:this

bind:this={DOM-узел}

Для получения ссылки на сам элемент в DOM, используйте bind:this.

<script>
	import { onMount } from 'svelte';

	let canvasElement;

	onMount(() => {
		const ctx = canvasElement.getContext('2d');
		drawStuff(ctx);
	});
</script>

<canvas bind:this={canvasElement}></canvas>

class:имя

class:имя={значение}
class:имя

Директива class: обеспечивает простой способ управления классами элемента.

<!-- Эти строки эквивалентны -->
<div class="{active ? 'active' : ''}">...</div>
<div class:active={active}>...</div>

<!-- Сокращение, при одинаковых имени и значении -->
<div class:active>...</div>

<!-- можно использовать сразу несколько переключателей классов -->
<div class:active class:inactive={!active} class:isAdmin>...</div>

use:действие

use:действие
use:действие={параметры}
action = (node: HTMLElement, parameters: any) => {
	update?: (parameters: any) => void,
	destroy?: () => void
}

Действия — это функции, которые вызываются при создании элемента. Они могут возвращать объект с методом destroy, который вызывается, когда этот элемент удаляется из DOM.

<script>
	function foo(node) {
		// при добавлении элемента в DOM
		// node — ссылка на элемент в DOM

		return {
			destroy() {
				// при удалении элемента из DOM
			}
		};
	}
</script>

<div use:foo></div>

Действие может иметь параметры. Если возвращаемый объект имеет метод update, он будет вызываться всякий раз, когда эти параметры изменяются, сразу же после изменения разметки.

Не беспокойтесь о том, что мы объявляем функцию foo в каждом экземпляре компонента — Svelte выведет из контекста компонента любые функции, которые не зависят от его локального состояния.

<script>
	export let bar;

	function foo(node, bar) {
		// при добавлении элемента в DOM
		// node — ссылка на элемент в DOM

		return {
			update(bar) {
				// при изменении значения параметра `bar`
			},

			destroy() {
				// при удалении элемента из DOM
			}
		};
	}
</script>

<div use:foo={bar}></div>

transition:fn

transition:fn
transition:fn={параметры}
transition:fn|local
transition:fn|local={параметры}
transition = (node: HTMLElement, params: any) => {
	delay?: number,
	duration?: number,
	easing?: (t: number) => number,
	css?: (t: number, u: number) => string,
	tick?: (t: number, u: number) => void
}

Переход инициируется добавлением или удалением элемента из DOM в результате изменения состояния приложения.

Элементы внутри исчезающего блока будут храниться в DOM до тех пор, пока не будут выполнены все запущенные переходы.

Директива transition: указывает на то, что переход является обратимым. Т.е. он может в любой момент начать проигрываться в обратную сторону.

{#if visible}
	<div transition:fade>
		появляется и исчезает
	</div>
{/if}

По умолчанию переход появления не проигрывается при первой отрисовке компонента. Вы можете изменить это поведение установив параметр intro: true при создании компонента.

Параметры перехода

Как и действия, переходы могут иметь параметры.

(Двойные фигурные скобки {{...}} не являются особым синтаксисом; это просто литерал объекта внутри тега выражения)

{#if visible}
	<div transition:fade="{{ duration: 2000 }}">
		Влетает, исчезает. По 2 секунды на каждый переход
	</div>
{/if}
Пользовательские переходы

Переходы могут использовать пользовательские функции. Если возвращённый объект имеет функцию css, Svelte создаст CSS-анимацию, которая воспроизводится на элементе.

Аргумент t, передаваемый в функцию css, представляет собой значение между 0 и 1 после применения функции плавности easing. Переходы появления выполняются от 0 до1, переходы исчезновения выполняются от 1 до 0 — другими словами, 1 - это естественное состояние элемента, как если бы переход не применялся. Аргумент u равен 1 - t.

Функция вызывается множество раз с разными аргументами t и u до начала перехода.

<script>
	import { elasticOut } from 'svelte/easing';

	export let visible;

	function whoosh(node, params) {
		const existingTransform = getComputedStyle(node).transform.replace('none', '');

		return {
			delay: params.delay || 0,
			duration: params.duration || 400,
			easing: params.easing || elasticOut,
			css: (t, u) => `transform: ${existingTransform} scale(${t})`
		};
	}
</script>

{#if visible}
	<div in:whoosh>
		whooshes
	</div>
{/if}

Пользовательская функция перехода также может возвращать функцию tick, которая вызывается во время перехода с теми же аргументамиt и u.

Если возможно использовать css вместоtick, используйте — CSS-анимация может запускаться вне основного потока, предотвращая лаги на медленных устройствах.

<script>
	export let visible = false;

	function typewriter(node, { speed = 50 }) {
		const valid = (
			node.childNodes.length === 1 &&
			node.childNodes[0].nodeType === 3
		);

		if (!valid) return {};

		const text = node.textContent;
		const duration = text.length * speed;

		return {
			duration,
			tick: (t, u) => {
				const i = ~~(text.length * t);
				node.textContent = text.slice(0, i);
			}
		};
	}
</script>

{#if visible}
	<p in:typewriter="{{ speed: 20 }}">
		Съешь ещё этих мягких французских булок, да выпей же чаю
	</p>
{/if}

Если переход возвращает функцию вместо объекта перехода, то она будет вызвана в следующей микрозадаче. Это позволяет координировать несколько переходов, что дает возможность запускать перекрёстные переходы.

События переходов

Элемент, который имеет переходы, в дополнение к стандартным событиям DOM может запускать ещё и такие события:

  • introstart
  • introend
  • outrostart
  • outroend
{#if visible}
	<p
		transition:fly="{{ y: 200, duration: 2000 }}"
		on:introstart="{() => status = 'начало появления'}"
		on:outrostart="{() => status = 'начало исчезновения'}"
		on:introend="{() => status = 'конец появления'}"
		on:outroend="{() => status = 'конец исчезновения'}"
	>
		Прилетает и улетает
	</p>
{/if}

Локальные переходы воспроизводятся только когда создается или убирается конкретный блок, к которому они прикреплены, а не его родительские блоки.

{#if x}
	{#if y}
		<p transition:fade>
			проигрывается когда изменяются 'x' или 'y'
		</p>

		<p transition:fade|local>
			проигрывается только когда изменяется 'y'
		</p>
	{/if}
{/if}

in:fn/out:fn

in:fn
in:fn={параметры}
in:fn|local
in:fn|local={параметры}
out:fn
out:fn={параметры}
out:fn|local
out:fn|local={параметры}

Аналогичны transition:, но применяются только когда элемент появляется (in:) или убирается (out:) из DOM.

В отличие от transition:, переходы in: и out: не являются обратимыми. При исчезновении элемента, если переход появления ещё не закончился, он будет проигрываться дальше, но уже вместе с переходом исчезновения. Если исчезновение элемента, было прервано, то переходы будут запущены заново.

{#if visible}
	<div in:fly out:fade>
		влетает, исчезает
	</div>
{/if}

animate:

animate:имя
animate:имя={параметры}
animation = (node: HTMLElement, { from: DOMRect, to: DOMRect } , params: any) => {
	delay?: number,
	duration?: number,
	easing?: (t: number) => number,
	css?: (t: number, u: number) => string,
	tick?: (t: number, u: number) => void
}
DOMRect {
	bottom: number,
	height: number,
	​​left: number,
	right: number,
	​top: number,
	width: number,
	x: number,
	y:number
}

Анимация запускается, когда изменяется порядок содержимого блока each с ключом. Анимации не запускаются при удалении элемента, а только лишь при переупорядочивании данных внутри блока each. Директиву animate можно устанавливать только для элемента, который является непосредственным дочерним элементом блока each с ключом.

Анимация может использовать встроенные функции анимации или пользовательские функции анимации.

<!-- при изменении порядка элементов в списке запустится анимация-->
{#each list as item, index (item)}
	<li animate:flip>{item}</li>
{/each}
Параметры анимации

Как действия и переходы, анимации также могут иметь параметры.

(Двойные фигурные скобки {{...}} здесь не являются особым синтаксисом - это просто литерал объекта внутри тега выражения)

{#each list as item, index (item)}
	<li animate:flip="{{ delay: 500 }}">{item}</li>
{/each}
Пользовательские функции анимации

Анимации могут использовать пользовательские функции, которые принимают в качестве аргументов ссылку на элемент node, объект animation и объект параметров. Объект animation содержит свойстваfrom и to, каждое из которых является объектом DOMRect, описывающим геометрию элемента в начальном и конечном положениях. Свойство from - это DOMRect элемента в его начальной позиции, свойство to - это DOMRect элемента в его конечной позиции после переупорядочивания списка и обновления DOM.

Если в возвращаемом из функции объекте есть метод css, Svelte создаст CSS-анимацию, которая будет применена к элементу node.

Аргумент t, передаваемый в метод css, представляет собой значение от 0 до 1, которое возвращает функция плавности easing для нужного момента времени. Аргумент u равен 1 - t.

Функция вызывается множество раз до начала анимации, с различными аргументами t и u.

<script>
	import { cubicOut } from 'svelte/easing';

	function whizz(node, { from, to }, params) {

		const dx = from.left - to.left;
		const dy = from.top - to.top;

		const d = Math.sqrt(dx * dx + dy * dy);

		return {
			delay: 0,
			duration: Math.sqrt(d) * 120,
			easing: cubicOut,
			css: (t, u) =>
				`transform: translate(${u * dx}px, ${u * dy}px) rotate(${t*360}deg);`
		};
	}
</script>

{#each list as item, index (item)}
	<div animate:whizz>{item}</div>
{/each}

Пользовательская функция анимации также может возвращать функцию tick, которая вызывается во время анимации с теми же аргументами t и u.

Всегда старайтесь использоватьcss вместоtick, поскольку CSS-анимация запускается вне основного потока, предотвращая подёргивания на медленных устройствах.

<script>
	import { cubicOut } from 'svelte/easing';

	function whizz(node, { from, to }, params) {

		const dx = from.left - to.left;
		const dy = from.top - to.top;

		const d = Math.sqrt(dx * dx + dy * dy);

		return {
		delay: 0,
		duration: Math.sqrt(d) * 120,
		easing: cubicOut,
		tick: (t, u) =>
			Object.assign(node.style, {
				color: t > 0.5 ? 'Pink' : 'Blue'
			});
	};
	}
</script>

{#each list as item, index (item)}
	<div animate:whizz>{item}</div>
{/each}

Директивы компонентов

on:событие

on:событие={обработчик}

Компоненты могут отправлять события, используя диспетчер событий createEventDispatcher или пробрасывая события DOM. Обработка событий компонента выглядит так же, как обработка событий DOM.

<SomeComponent on:whatever={handler}/>

Если директива on: используется без значения, то компонент будет пробрасывать событие выше, как и в аналогичном случае с событиями DOM. Событие станет доступно для прослушивания в родительском компоненте.

<SomeComponent on:whatever/>

bind:свойство

bind:свойство={переменная}

К свойствам компонента можно привязаться, точно так же как к атрибутам элементов.

<Keypad bind:value={pin}/>

bind:this

bind:this={экземпляр_компонента}

Компоненты также поддерживают привязку bind:this, позволяющую взаимодействовать с экземпляром компонента в коде.

Обратите внимание, что использование {cart.empty} вызовет ошибку, поскольку при первой отрисовке кнопки cart ещё имеет значение undefined.

<ShoppingCart bind:this={cart}/>

<button on:click={() => cart.empty()}>
	Очистить корзину
</button>

<slot>

<slot><!-- содержимое по умолчанию --></slot>
<slot name="x"><!-- содержимое по умолчанию --></slot>
<slot свойство={значениие}></slot>

Как и любой HTML-элемент, компоненты могут иметь вложенные элементы.

Вложенное содержимое размещается в компоненте при помощи элемента <slot>, который также может содержать содержимое по умолчанию, которое отображается, если в компонент не было предано никакого содержимого.

<!-- App.svelte -->
<Widget>
	<p>вложенный элемент</p>
</Widget>

<!-- Widget.svelte -->
<div>
	<slot>
		это отобразится, если использовать просто '<Widget/>'
	</slot>
</div>

<slot name="имя">

Именованные слоты позволяют указать конкретные области. Они тоже могут иметь запасное содержимое по умолчанию.

<!-- App.svelte -->
<Widget>
	<h1 slot="header">Привет</h1>
	<p slot="footer">Все права (c) 2019 Svelte Industries</p>
</Widget>

<!-- Widget.svelte -->
<div>
	<slot name="header">Заголовок не предоставлен</slot>
	<p>Любое содержимое между заголовком и футером</p>
	<slot name="footer"></slot>
</div>

<slot let:имя={значение}>

Слоты могут быть отрисованы ноль или более раз и могут передавать значения обратно родителю через свои свойства. Родитель может получить эти значения от слота при помощи директивы let:.

Обычные правила сокращения работают и тут — let:item то же самое, что и let:item={item}, а <slot {item}> эквивалентно <slot item={item}>.

<!-- App.svelte -->
<FancyList {items} let:item={item}>
	<div>{item.text}</div>
</FancyList>

<!-- FancyList.svelte -->
<ul>
	{#each items as item}
		<li class="fancy">
			<slot item={item}></slot>
		</li>
	{/each}
</ul>

Именованные слоты также могут предоставлять значения. Директива let: указывается на элементе с атрибутом slot.

<!-- App.svelte -->
<FancyList {items}>
	<div slot="item" let:item={item}>{item.text}</div>
	<p slot="footer">Все права (c) 2019 Svelte Industries</p>
</FancyList>

<!-- FancyList.svelte -->
<ul>
	{#each items as item}
		<li class="fancy">
			<slot name="item" item={item}></slot>
		</li>
	{/each}
</ul>

<slot name="footer"></slot>

<svelte:self>

Элемент <svelte: self> позволяет компоненту включать самого себя себя рекурсивно.

Он не может отображаться на верхнем уровне разметки, а должен быть помещён внутри блока if или each, чтобы избежать бесконечного цикла.

<script>
	export let count;
</script>

{#if count > 0}
	<p>Отсчёт... {count}</p>
	<svelte:self count="{count - 1}"/>
{:else}
	<p>Поехали!</p>
{/if}

<svelte:component>

<svelte:component this={выражение}/>

Элемент <svelte:component> отрисовывает компонент динамически, используя конструктор компонента, переданный в свойствеthis. Когда значение свойства изменяется, компонент уничтожается и создается заново.

Если выражение в свойстве this ложное, компонент не отрисовывается.

<svelte:component this={currentSelection.component} foo={bar}/>

<svelte:window>

<svelte:window on:событие={обработчик}/>
<svelte:window bind:свойство={значение}/>

Элемент <svelte:window> позволяет добавлять обработчики событий к объекту window, не беспокоясь об их удалении при уничтожении компонента или проверять существование window при рендеринге на стороне сервера.

<script>
	function handleKeydown(event) {
		alert(`нажата клавиша ${event.key}`);
	}
</script>

<svelte:window on:keydown={handleKeydown}/>

Также можно сделать привязку к следующим свойствам:

  • innerWidth
  • innerHeight
  • outerWidth
  • outerHeight
  • scrollX
  • scrollY
  • online — сокращение для window.navigator.onLine

Все свойства, кроме scrollX и scrollY доступны только для чтения.

<svelte:window bind:scrollY={y}/>

<svelte:body>

<svelte:body on:событие={обработчик}/>

Как и в случае с <svelte:window>, этот элемент позволяет добавлять обработчики событий к document.body, например к mouseenter иmouseleave, которые не запускаются в window.

<svelte:body
	on:mouseenter={handleMouseenter}
	on:mouseleave={handleMouseleave}
/>

<svelte:head>

<svelte:head>...</svelte:head>

Этот элемент позволяет вставлять элементы в document.head. При рендеринге на стороне сервера содержимое head предоставляется отдельно от основного содержимого html.

<svelte:head>
	<link rel="stylesheet" href="tutorial/dark-theme.css">
</svelte:head>

<svelte:options>

<svelte:options параметр={значение}/>

Элемент <svelte:options> позволяет установить определенные параметры для компилятора для каждого отдельного компонента. Подробнее о параметрах компилятора можно узнать в разделе про функцию compile. Параметры, которые можно установить:

  • immutable={true} — установите, если вы нигде не используете изменяемые данные — тогда компилятор сможет выполнять более простые проверки равенства объектов для определения их изменения
  • immutable={false} — по умолчанию. Svelte будет проверять изменение объектов обычным способом
  • accessors={true} — добавляет сеттеры и геттеры для свойств компонента
  • accessors={false} — по умолчанию, аксессоры не добавляются
  • namespace="..." — пространство имен, где компонент будет использован (обычно нужно "svg")
  • tag="..." — имя, которое используется при компиляции компонента в пользовательский элемент
<svelte:options tag="my-custom-element"/>

Рантайм

svelte

Пакет svelte предоставляет функции жизненного цикла и API контекста.

onMount

onMount(callback: () => void)
onMount(callback: () => () => void)

Функция onMount запланирует запуск своей callback-функции, как только компонент будет смонтирован в DOM. Эта функцция должна быть вызвана только во время инициализации компонента (но она не обязана находится внутри компонента; её можно вызывать и из внешнего модуля).

onMount не запускается для компонентов на стороне сервера.

<script>
	import { onMount } from 'svelte';

	onMount(() => {
		console.log('компонент смонтирован');
	});
</script>

Если onMount возвращает функцию, то она будет вызвана при удалении компонента из DOM.

<script>
	import { onMount } from 'svelte';

	onMount(() => {
		const interval = setInterval(() => {
			console.log('beep');
		}, 1000);

		return () => clearInterval(interval);
	});
</script>

beforeUpdate

beforeUpdate(callback: () => void)

Запланирует запуск своей callback-функции непосредственно перед обновлением компонента после любого изменения состояния приложения.

Первый раз функция в beforeUpdate сработает непосредственно перед запуском onMount

<script>
	import { beforeUpdate } from 'svelte';

	beforeUpdate(() => {
		console.log('компонент сейчас обновится');
	});
</script>

afterUpdate

afterUpdate(callback: () => void)

Запланирует запуск своей callback-функции сразу после обновления компонента.

<script>
	import { afterUpdate } from 'svelte';

	afterUpdate(() => {
		console.log('компонент только что обновился');
	});
</script>

onDestroy

onDestroy(callback: () => void)

Запланирует запуск своей callback-функции при удалении компонента из DOM.

Из всех функций жизненного цикла onMount, beforeUpdate, afterUpdate и onDestroy, эта единственная, которая запускается в при рендеринге на стороне сервера.

<script>
	import { onDestroy } from 'svelte';

	onDestroy(() => {
		console.log('компонент удаляется');
	});
</script>

tick

promise: Promise = tick()

Возвращает промис, который выполняется после применения всех ожидающих изменений состояния приложения либо в следующей микрозадаче, если таковые отсутствуют.

<script>
	import { beforeUpdate, tick } from 'svelte';

	beforeUpdate(async () => {
		console.log('компонент сейчас будет обновляться');
		await tick();
		console.log('компонент обновился');
	});
</script>

setContext

setContext(key: any, context: any)

Связывает произвольный объект context с текущим компонентом и указанным ключом key. После этого, при помощи метода getContext, контекст становится доступным для всех дочерних элементов компонента (включая содержимое слотов).

Как и функции жизненного цикла, этот метод должен вызываться во время инициализации компонента.

<script>
	import { setContext } from 'svelte';

	setContext('answer', 42);
</script>

getContext

context: any = getContext(key: any)

Извлекает контекст, который был объявлен с указананным ключом в ближайшем родительском компоненте. Этот метод также должен вызываться во время инициализации компонента.

<script>
	import { getContext } from 'svelte';

	const answer = getContext('answer');
</script>

createEventDispatcher

dispatch: ((name: string, detail?: any) => void) = createEventDispatcher();

Создает диспетчер событий, который можно использовать для отправки событий компонента. Диспетчер событий — это функция, которая может принимать два аргумента: name иdetail.

События компонента созданные при помощи метода createEventDispatcher создают пользовательские события CustomEvent. Эти события не всплывают и не могут быть отменены методом event.preventDefault(). Аргумент detail соответствует свойству CustomEvent.detail и может содержать данные любого типа.

<script>
	import { createEventDispatcher } from 'svelte';
	const dispatch = createEventDispatcher();
</script>

<button on:click="{() => dispatch('notify', 'любые данные')}">Запустить событие</button>

События, отправленные из дочернего компонента, можно прослушивать в их родительском компоненте. Любые данные, указанные при отправке события, будут доступны в свойстве detail объекта события.

<script>
	function callbackFunction(event) {
		console.log(`Событие запущено! Данные: ${event.detail}`)
	}
</script>

<Child on:notify="{callbackFunction}"/>

svelte/store

Модуль svelte/store предоставляет функции для создания хранилищ.

Чтобы считаться хранилищем, объект должен иметь метод subscribe, который возвращает функцию unsubscribe.

const unsubscribe = store.subscribe(value => {
	console.log(value);
}); // вывод значения `value`

// позднее...
unsubscribe();

Хранилища имеют особую выразительность внутри компонентов Svelte. Их значения можно получить, поставив перед именем хранилища символ $, что заставляет Svelte автоматически подписываться и отписываться от хранилища в течение жизненного цикла компонента.

<script>
	import { count } from './stores.js';

	function handleClick() {
		// это эквивалентно count.update(n => n + 1)
		$count += 1;
	}
</script>

<button on:click={handleClick}>
	Кликов: {$count}
</button>

writable

store = writable(value: any)
store = writable(value: any, (set: (value: any) => void) => () => void)

Создает хранилище с дополнительными методами set и update.

import { writable } from 'svelte/store';

const count = writable(0);

count.subscribe(value => {
	console.log(value);
}); // выведет '0'

count.set(1); // выведет '1'

count.update(n => n + 1); // выведет '2'

Если в качестве второго аргумента передается функция, она вызывается, когда число подписчиков меняется с ноля на единицу (но не с одного на два и т.д.). Этой функции будет передана функция set, которая задаёт значение хранилища. Также она должна возвращать функцию stop, которая вызывается, когда счётчик подписчиков изменяется с одного на ноль.

import { writable } from 'svelte/store';

const count = writable(0, () => {
	console.log('у нас есть подписчик');
	return () => console.log('подписчиков не осталось');
});

count.set(1); // ничего не делает

const unsubscribe = count.subscribe(value => {
	console.log(value);
}); // выводит 'у нас есть подписчик', потом '1'

unsubscribe(); // выводит 'подписчиков не осталось'

readable

store = readable(value: any, (set: (value: any) => void) => () => void)

Создает хранилище, значение которого нельзя установить извне. Первый аргумент задаёт начальное значение хранилища.

Второй аргумент в readable такой же, как второй аргумент в writable, за исключением того, что он является обязательным для readable (в противном случае не было бы возможности обновить значение хранилища).

import { readable } from 'svelte/store';

const time = readable(new Date(), set => {
	const interval = setInterval(() => {
		set(new Date());
	}, 1000);

	return () => clearInterval(interval);
});

derived

store = derived(a, callback: (a: any) => any)
store = derived(a, callback: (a: any, set: (value: any) => void) => void | () => void, initial_value: any)
store = derived([a, ...b], callback: ([a: any, ...b: any[]]) => any)
store = derived([a, ...b], callback: ([a: any, ...b: any[]], set: (value: any) => void) => void | () => void, initial_value: any)

Создаёт производное хранилище, на основе одного или нескольких других хранилищ. Всякий раз, когда меняются значения отслеживаемых хранилищ, выполняется callback-функция.

В самом простом случае в derived передаётся одно хранилище, а из callback-функции возвращается производное значение.

import { derived } from 'svelte/store';

const doubled = derived(a, $a => $a * 2);

Callback-функция может устанавливать значение асинхронно, принимая второй аргумент set и вызывая его при необходимости.

В этом случае, также можно передать третий аргумент в derived, которое будет начальным значением производного хранилища до первого вызова метода set.

import { derived } from 'svelte/store';

const delayed = derived(a, ($a, set) => {
	setTimeout(() => set($a), 1000);
}, 'секундочку...');

Если из callback-функции возвращается какая-либо функция, то она будет вызвана, когда callback-функция сработает снова или от хранилища отпишется последний подписчик:

import { derived } from 'svelte/store';
const tick = derived(frequency, ($frequency, set) => {
	const interval = setInterval(() => set(Date.now()), 1000 / frequency);
	return () => {
		clearInterval(interval);
	}
}, 'секундочку...');

В качестве первого аргумента может быть передан массив хранилищ.

import { derived } from 'svelte/store';

const summed = derived([a, b], ([$a, $b]) => $a + $b);

const delayed = derived([a, b], ([$a, $b], set) => {
	setTimeout(() => set($a + $b), 1000);
});

get

value: any = get(store)

Обычно, нужно получить значение хранилища, подписавшись на него, затем использовать его в нужных местах, с учетом того, что оно может изменяться со временем. Иногда может понадобиться просто получить значение хранилища, на которое вы не подписывались. get позволяет сделать это.

Этот метод просто подписывается на хранилище, получает значение и отписывается. Поэтому, не рекомендуется использовать его в высоконагружненых частях кода.

import { get } from 'svelte/store';

const value = get(store);

svelte/motion

Модуль svelte/motion экспортирует две функции, tweened и spring, для создания записываемых хранилищ, чьи значения, при изменении функциями set и update, меняются постепенно в течение какого-то количества времени, а не моментально.

tweened

store = tweened(value: any, options)

Хранилище, которое обновляет свои значения в течение фиксированного периода времени. Доступны следующие опции:

  • delay (number, по умолчанию 0) — миллисекунды до начала изменения
  • duration (number, по умолчанию 400) — длительность изменения в миллисекундах
  • easing (function, по умолчанию t => t) — функция плавности
  • interpolator (function) — смотри ниже

Методы store.set и store.update могут принимать второй аргумент options, который может перезаписать параметры, установленные при инициализации хранилища.

Обе функции возвращают промис, которое выполняется, когда изменение завершается. Если изменение прервать, промис никогда не будет выполнен.

Из коробки Svelte умеет рассчитывать изменения между двумя числами, двумя массивами или двумя объектами (при условии, что массивы и объекты имеют одинаковую структуру, а все окончания ветвлений свойств также являются числами).

<script>
	import { tweened } from 'svelte/motion';
	import { cubicOut } from 'svelte/easing';

	const size = tweened(1, {
		duration: 300,
		easing: cubicOut
	});

	function handleClick() {
		// эквивалентно вызову size.update(n => n + 1)
		$size += 1;
	}
</script>

<button
	on:click={handleClick}
	style="transform: scale({$size}); transform-origin: 0 0"
>embiggen</button>

Опция interpolator позволяет вам рассчитывать промежуточные значения между любыми произвольными значениями. Это должна быть функция (a, b) => t => value, гдеa - начальное значение, b - конечное значение,t - число от 0 до 1 и value это результат. Например, мы можем использовать пакет d3-interpolate для плавного перехода между двумя цветами.

<script>
	import { interpolateLab } from 'd3-interpolate';
	import { tweened } from 'svelte/motion';

	const colors = [
		'rgb(255, 62, 0)',
		'rgb(64, 179, 255)',
		'rgb(103, 103, 120)'
	];

	const color = tweened(colors[0], {
		duration: 800,
		interpolate: interpolateLab
	});
</script>

{#each colors as c}
	<button
		style="background-color: {c}; color: white; border: none;"
		on:click="{e => color.set(c)}"
	>{c}</button>
{/each}

<h1 style="color: {$color}">{$color}</h1>

spring

store = spring(value: any, options)

Хранилище spring постепенно меняет свое значение на основе параметров stiffness(жёсткость) и damping(затухание), получаются колебания по типу движения пружины. В отличие от хранилищ типа tweened, где значение меняется строго определенное количество времени, хранилища типа spring изменяют значение в течение продолжительности, которая задается их текущей скоростью, что позволяет более естественно выглядеть во многих ситуациях. Доступны следующие опции:

  • stiffness (number, по умолчанию 0.15) — значение от 0 до 1, чем больше, тем туже пружина
  • damping (number, по умолчанию 0.8) — значение 0 до 1, чем меньше тем пружиннее пружина
  • precision (number, по умолчанию 0.001) — определяет порог, при котором колебания пружины прекращаются, чем меньше, тем точнее

Как и в случае с tweened хранилищами, set и update возвращают промис, который выполняется, когда колебания прекратятся. Свойства store.stiffness иstore.damping могут быть изменены, даже во время колебаний и применяются немедленно.

Оба метода set и update могут принимать второй аргумент — объект со свойствами hard или soft. { hard: true } устанавливает новое значение немедленно; { soft: n } сохраняет существующий импульс в течение n секунд перед установкой значения. { soft: true } эквивалентно { soft: 0.5 }.

Посмотрите полноценный пример.

<script>
	import { spring } from 'svelte/motion';

	const coords = spring({ x: 50, y: 50 }, {
		stiffness: 0.1,
		damping: 0.25
	});
</script>

svelte/transition

Модуль svelte / transition экспортирует шесть функций:fade, fly,slide, scale,draw и crossfade. Они предназначены для использования в переходах.

fade

transition:fade={параметры}
in:fade={параметры}
out:fade={параметры}

Анимирует прозрачность элемента от 0 до установленной прозрачности для переходов in и от текущей прозрачности до 0 для переходов out.

fade принимает следующие параметры:

  • delay (number, по умолчанию 0) — задержка до начала перехода в миллисекундах
  • duration (number, по умолчанию 400) — длительность перехода в миллисекундах

Вы можете посмотреть переход fade в действии в соответствующем разделе учебника.

<script>
	import { fade } from 'svelte/transition';
</script>

{#if condition}
	<div transition:fade="{{delay: 250, duration: 300}}">
		появляется и исчезает
	</div>
{/if}

fly

transition:fly={параметры}
in:fly={параметры}
out:fly={параметры}

Анимирует позицию и прозрачность элемента. Переход появления in осуществляет анимацию перемещения элемента из текущей (по умолчанию) позиции на указанное в параметрах расстояние по x и y. Переход исчезновения out анимирует перемещение элемента из указанной параметрами позиции в его естественное состояние.

fly принимает следующие параметры:

  • delay (number, по умолчанию 0) — задержка начала перехода в миллисекундах
  • duration (number, по умолчанию 400) — длительность перехода в миллисекундах
  • easing (function, по умолчанию cubicOut) — функция плавности
  • x (number, по умолчанию 0) — сдвиг по оси x, конечная позиция для out и начальная для in
  • y (number, по умолчанию 0) — сдвиг по оси y, конечная позиция для out и начальная для in
  • opacity (number, по умолчанию 0) — значение прозрачности, конечное для out и начальное для in

Вы можете посмотреть переход fadflye в действии в соответствующем разделе учебника.

<script>
	import { fly } from 'svelte/transition';
	import { quintOut } from 'svelte/easing';
</script>

{#if condition}
	<div transition:fly="{{delay: 250, duration: 300, x: 100, y: 500, opacity: 0.5, easing: quintOut}}">
		прилетает и улетает
	</div>
{/if}

slide

transition:slide={параметры}
in:slide={параметры}
out:slide={параметры}

Сворачивает и разворачивает элемент.

slide принимает следующие параметры:

  • delay (number, по умолчанию 0) — задержка начала перехода в миллисекундах
  • duration (number, по умолчанию 400) — длительность перехода в миллисекундах
  • easing (function, по умолчанию cubicOut) — функция плавности
<script>
	import { slide } from 'svelte/transition';
	import { quintOut } from 'svelte/easing';
</script>

{#if condition}
	<div transition:slide="{{delay: 250, duration: 300, easing: quintOut }}">
		разворачивается и сворачивается
	</div>
{/if}

scale

transition:scale={параметры}
in:scale={параметры}
out:scale={параметры}

Анимирует прозрачность и размер элемента. Переход появления in осуществляет анимацию элемента из текущего (по умолчанию) состояния в состояние, указанное через параметры. Переход исчезновения out анимирует изменение состояния элемента из заданного параметрами в естественное состояние.

scale принимает следующие параметры:

  • delay (number, по умолчанию 0) — задержка начала перехода в миллисекундах
  • duration (number, по умолчанию 400) — длительность перехода в миллисекундах
  • easing (function, по умолчанию cubicOut) — функция плавности
  • start (number, по умолчанию 0) — размер, конечный для out и начальный для in
  • opacity (number, по умолчанию 0) — значение прозрачности, конечное для out и начальное для in
<script>
	import { scale } from 'svelte/transition';
	import { quintOut } from 'svelte/easing';
</script>

{#if condition}
	<div transition:scale="{{duration: 500, delay: 500, opacity: 0.5, start: 0.5, easing: quintOut}}">
		увеличивается и уменьшается
	</div>
{/if}

draw

transition:draw={параметры}
in:draw={параметры}
out:draw={параметры}

Анимация закрашивания элемента в SVG. Переход появления in начинается с невидимого элемента path, который затем постепенно закрашивается. Переход исчезновения out начинается с видимого элемента path, который затем постепенно стирается. draw работает только с элементами, у которых есть метод getTotalLength, например <path> и <polyline>.

draw принимает следующие параметры:

  • delay (number, по умолчанию 0) — задержка начала перехода в миллисекундах
  • speed (number, по умолчанию undefined) - скорость анимации, см. ниже
  • duration (number | function, по умолчанию 800) — длительность перехода в миллисекундах
  • easing (function, по умолчанию cubicInOut) — функция плавности

Параметр speed задаёт длительность перехода в зависимости от длины элемента path. Это модификатор, который применяется к длине пути: длительность = длина/скорость. Отрисовка линии в 1000 пикселей со скоростью 1 будет иметь длительность 1000мс, при speed равном 0.5 длительность увеличится вдвое, а при 2 — уменьшится в два раза.

<script>
	import { draw } from 'svelte/transition';
	import { quintOut } from 'svelte/easing';
</script>

<svg viewBox="0 0 5 5" xmlns="http://www.w3.org/2000/svg">
	{#if condition}
		<path transition:draw="{{duration: 5000, delay: 500, easing: quintOut}}"
					d="M2 1 h1 v1 h1 v1 h-1 v1 h-1 v-1 h-1 v-1 h1 z"
					fill="none"
					stroke="cornflowerblue"
					stroke-width="0.1px"
					stroke-linejoin="round"
		/>
	{/if}
</svg>

svelte/animate

Модуль svelte/animate экспортирует единственную функцию, которая используется для отображения анимации.

flip

animate:flip={параметры}

Функция flip вычисляет начальную и конечную позиции элемента и создаёт анимацию перемещения между ними, подставляя соответствующие значения x и y. Аббревиатура flip расшифровывается как First, Last, Invert, Play.

flip принимает следующие параметры:

  • delay (number, по умолчанию 0) — миллисекунды до начала анимации
  • duration (number | function, по умолчанию d => Math.sqrt(d) * 120) — длительность анимации, см.ниже
  • easing (function, по умолчанию cubicOut) — функция плавности

duration может быть передана двумя способами:

  • просто число, в миллисекундах.
  • функция, distance: number => duration: number, которая получает расстояние в пикселях, на которое элемент должен переместится и возвращает длительность в миллисекундах. Она позволяет задавать длительность анимации для разных элементов в зависимости от расстояния перемещения для каждого из них.

Вы можете познакомиться с полноценным примером в разделе учебника.

<script>
	import { flip } from 'svelte/animate';
	import { quintOut } from 'svelte/easing';
	let list = [1, 2, 3];
</script>

{#each list as n (n)}
	<div animate:flip="{{delay: 250, duration: 250, easing: quintOut}}">
		{n}
	</div>
{/each}

svelte/easing

  • TODO could have nice little interactive widgets showing the different functions, maybe

svelte/register

Чтобы отрендерить компонент Svelte в Node.js без сборки, используйте require('svelte/register'). После этого можно импортировать любой файл .svelte при помощи require.

require('svelte/register');
const App = require('./App.svelte').default;
...
const { html, css, head } = App.render({ answer: 42 });

Свойство .default необходимо, потому что происходит преобразование из нативных модулей JavaScript в модули CommonJS, используемые в Node. Учтите, что если в компоненте есть импорты JavaScript-модулей, то они не смогут загрузиться в Node, и тогда придётся использовать сборщик.

Чтобы установить параметры компиляции или использовать собственное расширение файла, вызовите хук register как функцию:

require('svelte/register')({
  extensions: ['.customextension'], // по умолчанию ['.html', '.svelte']
	preserveComments: true
});

API компонента на клиенте

Создание компонента

const component = new Component(options)

Компонент на стороне клиента — это компонент, скомпилированный с помощью метода generate: 'dom'(или опция generate не была указана) и являющийся JavaScript классом.

import App from './App.svelte';

const app = new App({
	target: document.body,
	props: {
		// предполагается, что App.svelte содержит что-то вроде
		// `export let answer`:
		answer: 42
	}
});

Могут быть указаны следующие параметры инициализации:

Параметр По умолчанию Описание
target none HTML элемент в который необходимо отрисовать компонент. Обязательный.
anchor null Потомок target, перед которым будет отрисован компонент
props {} Объект свойств для передачи компоненту
hydrate false См.ниже
intro false Если true, будет отыгрывать переходы при начальной отрисовке, а не ждать последующих изменений состояния

Существующие вложенные элементы в target остаются там, где они есть.

Параметр hydrate инструктирует Svelte обновить существующий DOM (обычно полученный в ходе рендеринга на стороне сервера), а не создавать новые элементы. Это будет работать только в том случае, если компонент был скомпилирован с параметром hydratable: true.

В то время как дочерние элементы target обычно нормально остаются одни,hydrate: true приведет к удалению всех детей. По этой причине опция anchor не может использоваться вместе сhydrate: true.

Существующий DOM не обязан полностью совпадать с компонентом - Svelte будет 'чинить' структуру DOM по мере необходимости.

import App from './App.svelte';

const app = new App({
	target: document.querySelector('#server-rendered-html'),
	hydrate: true
});

$set

component.$set(props)

Программно устанавливает свойство экземпляру компонента. Действие component.$set({ x: 1 }) эквивалентно присвоению x = 1 внутри блока <script> компонента.

Вызов этого метода запланирует обновление в следующей микрозадаче - DOM не обновляется синхронно.

component.$set({ answer: 42 });

$on

component.$on(event, callback)

Вызывает функцию callback каждый раз когда компонент отправляет событие event.

Возвращает функцию, при вызове которой обработчик события удаляется.

const off = app.$on('selected', event => {
	console.log(event.detail.selection);
});

off();

$destroy

component.$destroy()

Удаляет компонент из DOM и запускает все имеющиеся обработчики функции onDestroy.

Свойства компонента

component.prop
component.prop = value

Если компонент скомпилирован с параметром accessors: true, каждый экземпляр будет иметь геттеры и сеттеры, соответствующие каждому из компонентов компонента. Установка значения приведет к синхронному обновлению, а не к асинхронному обновлению по умолчанию, которое вызывается методом component.$set(...).

По умолчанию accessors имеет значениеfalse, если вы не компилируете компонент как пользовательский элемент.

console.log(app.count);
app.count += 1;

API пользовательского элемента

  • TODO

API компонента на сервере

const result = Component.render(...)

В отличие от компонентов на стороне клиента, компоненты га стороне сервера не имеют такого же жизненного цикла после их рендеринга - вся их работа заключается лишь в создании HTML и CSS. По этой причине API несколько отличается.

Компонент на стороне сервера предоставляет метод render, который можно вызывать, передав при необходимости нужные свойства. Он возвращает объект со свойствами head, html и css. При этом в head будет помещено содержимое всех имеющихся элементов <svelte:head>.

const App = require('./App.svelte');

const { head, html, css } = App.render({
	answer: 42
});

Компиляция

В обычных условиях, вам не нужно напрямую взаимодействовать с компилятором Svelte, достаточно просто интегрировать его в вашу систему сборки используя один из следующих плагинов-бандлеров:

Тем не менее, полезно знать, как пользоваться компилятором, так как эти плагины обычно предоставляют возможность настройки его параметров.

svelte.compile

result: {
	js,
	css,
	ast,
	warnings,
	vars,
	stats
} = svelte.compile(source: string, options?: {...})

Тут происходит вся магия. Метод svelte.compile берет исходный код вашего компонента и превращает его в JavaScript модуль, который экспортирует класс.

const svelte = require('svelte/compiler');

const result = svelte.compile(source, {
	// параметры
});

Компилятору можно передать следующие параметры. Все они являются необязательными:

параметр по умолчанию описание
filename null string имя файла для подсказок при отладке и карт исходников. Плагин назначит имя автоматически.
name "Component" string имя JavaScript класса на выходе (если в области видимости будет конфликт имён, компилятор переименует класс). Обычно имя берётся из параметра filename.
format "esm" Значение "esm", создаёт JavaScript модуль (с import и export). Значение "cjs ", создаёт CommonJS модуль (с require и module.exports), который обычно полезен при рендеринге на стороне сервера или тестировании.
generate "dom" При значении "dom", Svelte создаёт JavaScript класс для встраивания в DOM. При значении "ssr", Svelte создаёт объект с методом render, подходящим для рендеринга на стороне сервера. Если указать false, JavaScript или CSS возвращаться не будут, только метаданные.
dev false При значении true, в компоненты будет встраиваться дополнительный код, который будет выполнять различные проверки и предоставлять отладочную информацию во время разработки.
immutable false Значение true, говорит компилятору, что вы обязуетесь не изменять структуру каких-либо объектов. Это позволяет отслеживать изменения значений более оптимальным путём.
hydratable false При значении true, позволяет установить параметр hydrate: true в среде выполнения, который позволяет компоненту обновлять уже существующий DOM, а не создавать новую структуру DOM с нуля.
legacy false При значении true будет генерироваться код, совместимый с IE9 и IE10, которые не поддерживают некоторые вещи, напримерelement.dataset.
accessors false Значение true заставляет генерировать для свойств компонента геттеры и сеттеры. При значении false, они будут создаваться только для экспортируемых значений доступных только для чтения (которые объявлены при помощи const, class или function). Если используется параметр customElement: true по умолчанию этот параметр будет равен true.
customElement false При значении true компилятор будет создавать конструктор пользовательского элемента, а не обычного Svelte компонента.
tag null имя тега, с которым нужно зарегистрировать пользовательский элемент. Это должна быть строка из маленьких букв и цифр с хотя бы одним дефисом, например "my-element".
css true Если значение равно true, стили включаются в сам класс JavaScript и будут динамически применены во время выполнения. Рекомендуется установить значение false и использовать статически сгенерированный CSS файл, поскольку это приведет к уменьшению JavaScript бандла и повышению производительности.
preserveComments false При значении true, ваши комментарии в HTML разметке будут сохранены при рендере на стороне сервера. По умолчанию они удаляются.
preserveWhitespace false При значении true, пробелы внутри и между элементами остаются так остаются не тронутыми. В ином случае, Svelte удалит лишние пробелы.
outputFilename null Имя файла для карты исходников JavaScript.
cssOutputFilename null Имя файла для карты исходников CSS.
sveltePath "svelte" Расположение пакета svelte. Любые импорты из svelte или svelte/[module] будут соответствующим образом обработаны.

Возвращаемый объект result содержит код вашего компонента и некоторые полезные метаданные.

const {
	js,
	css,
	ast,
	warnings,
	vars,
	stats
} = svelte.compile(source);
  • js и css — объекты со следующими свойствами:
    • code — код JavaScript
    • map — карта исходников с дополнительными удобными методами toString() и toUrl().
  • ast — абстрактное синтаксическое дерево, представляющее структуру компонента.
  • warnings — массив объектов предупреждений, которые могли возникнуть при компиляции. У каждый такого объекта есть несколько свойств:
    • code — идентификатор категории ошибки
    • message — описание проблемы в форме, понятной для человека
    • start и end — информация о предупреждениях, относящихся к конкретным местам в коде; являются объектами со свойствами line, column и character
    • frame — выделенная часть проблемного кода с номерами строк, если имеется
  • vars — массив всех переменных, объявленных в компоненте. Например, используется в eslint-plugin-svelte3. У каждого элемента есть несколько свойств:
    • name — имя переменной
    • export_name — имя переменной при экспорте (совпадает с name, если экспорт выполнен без использования конструкции export...as)
    • injected — равно true, если переменная была объявлена самим Svelte, а не в вашем коде
    • module — равно true, если переменная объявлена в блоке <script context="module">
    • mutated — равно true, переменной назначались свойства внутри компонента
    • reassigned — равно true, если переменная переназначалась внутри компонента
    • referencedtrue если значение используется вне контекста объявления
    • writable — равно true, если значение объявлено с помощью let или var (но не const, class или function)
  • stats — объект, используемый командой разработчиков Svelte для диагностики компилятора. Не полагайтесь на него в своём коде!

svelte.parse

ast: object = svelte.parse(
	source: string,
	options?: {
		filename?: string,
		customElement?: boolean
	}
)

Функция parse выполняет разбор компонента, возвращая только его абстрактное синтаксическое дерево. В отличие от компиляции с параметром generate: false, при этом не будет выполняться никакой проверки или иного анализа компонента, кроме самого разбора.

const svelte = require('svelte/compiler');
const ast = svelte.parse(source, { filename: 'App.svelte' });

svelte.preprocess

result: {
	code: string,
	dependencies: Array<string>
} = svelte.preprocess(
	source: string,
	preprocessors: Array<{
		markup?: (input: { source: string, filename: string }) => Promise<{
			code: string,
			dependencies?: Array<string>
		}>,
		script?: (input: { source: string, attributes: Record<string, string>, filename: string }) => Promise<{
			code: string,
			dependencies?: Array<string>
		}>,
		style?: (input: { source: string, attributes: Record<string, string>, filename: string }) => Promise<{
			code: string,
			dependencies?: Array<string>
		}>
	}>,
	options?: {
		filename?: string
	}
)

Функция preprocess предоставляет удобные хуки для произвольного преобразования исходного кода компонента. Например, его можно использовать для преобразования блока <style lang="sass"> в ванильный CSS.

Первый аргумент — это исходный код компонента. Второй — это массив препроцессоров preprocessors (или просто объект preprocessor, если нужен только один), где препроцессор — это объект с функциями markup,script и style, каждый из которых не является обязательным.

Каждая функция markup,script или style должна возвращать объект (или промис, который при выполнении возвращает объект) со свойством code, представляющим преобразованный исходный код, и необязательным массивом зависимостей dependencies.

Функция markup получает весь исходный текст компонента вместе с именем файла (filename) компонента, если он был указан в третьем аргументе.

Функции препроцессоров также могут дополнительно возвращать объект map вместе с code и dependencies, гдеmap — это карта исходников для отладки преобразованного кода. В текущих версиях Svelte этот объект игнорируется, но в будущих версиях Svelte сможет обрабатывать карты исходников от препроцессоров.

const svelte = require('svelte/compiler');

const { code } = svelte.preprocess(source, {
	markup: ({ content, filename }) => {
		return {
			code: content.replace(/foo/g, 'bar')
		};
	}
}, {
	filename: 'App.svelte'
});

Функции script и style получают содержимое блоков <script> и <style> соответственно. В дополнение к filename они получают объект атрибутов блока.

Если возвращается массив зависимостей dependencies, он будет также включен в результирующий объект. Он используется такими пакетами, как rollup-plugin-svelte для отслеживания изменений дополнительных файлов, в случае, если, например в теге <style> есть @import.

const svelte = require('svelte/compiler');
const sass = require('node-sass');
const { dirname } = require('path');

const { code, dependencies } = svelte.preprocess(source, {
	style: async ({ content, attributes, filename }) => {
		// обрабатываем только <style lang="sass">
		if (attributes.lang !== 'sass') return;

		const { css, stats } = await new Promise((resolve, reject) => sass.render({
			file: filename,
			data: content,
			includePaths: [
				dirname(filename),
			],
		}, (err, result) => {
			if (err) reject(err);
			else resolve(result);
		}));

		return {
			code: css.toString(),
			dependencies: stats.includedFiles
		};
	}
}, {
	filename: 'App.svelte'
});

Несколько препроцессоров могут быть использованы вместе. Выход первого становится входом для второго и так далее. Сначала запускаются функции markup, затем script и style.

const svelte = require('svelte/compiler');

const { code } = svelte.preprocess(source, [
	{
		markup: () => {
			console.log('это запустится первым');
		},
		script: () => {
			console.log('это запустится третьим');
		},
		style: () => {
			console.log('это запустится пятым');
		}
	},
	{
		markup: () => {
			console.log('это запустится вторым');
		},
		script: () => {
			console.log('это запустится четвертым');
		},
		style: () => {
			console.log('это запустится шестым');
		}
	}
], {
	filename: 'App.svelte'
});

svelte.walk

walk(ast: Node, {
	enter(node: Node, parent: Node, prop: string, index: number)?: void,
	leave(node: Node, parent: Node, prop: string, index: number)?: void
})

Функция walk обеспечивает способ обхода абстрактных синтаксических деревьев, сгенерированных парсером, используя встроенный в компилятор собственный экземпляр estree-walker ,

Метод walk принимает абстрактное синтаксическое дерево для обхода и объект с двумя необязательными методами: enter и exit. Для каждого узла дерева вызывается enter (если есть). Затем, если внутри enter не вызывается this.skip(), подобным образом обрабатываются дочерние узлы, затем вызывается метод exit.

const svelte = require('svelte/compiler');
svelte.walk(ast, {
	enter(node, parent, prop, index) {
		do_something(node);
		if (should_skip_children(node)) {
			this.skip();
		}
	},
	leave(node, parent, prop, index) {
		do_something_else(node);
	}
});

svelte.VERSION

Текущая версия Svelte, которая указана в файле package.json.

const svelte = require('svelte/compiler');
console.log(`используется Svelte версии ${svelte.VERSION}`);