Тег в нижнем регистре, например <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>
Булевые атрибуты присоединяются к элементу, если их значения истинное и убираются с него, когда значения являются ложными.
Все остальные атрибуты будут присоединены к элементу, только если их значение не является null-образным (null
или undefined
).
<input required={false} placeholder="Это поле ввода не является обязательным">
<div title={null}>У этого контейнера нет атрибута title</div>
Выражение может содержать символы, которые могут вызвать проблемы при подсветке синтаксиса в обычном HTML, поэтому допускается использование значения в кавычках. Кавычки не влияют на то, как анализируется значение:
<button disabled="{number !== 42}">...</button>
Когда имя и значение атрибута совпадают (name = {name}
) — их можно заменить на {name}
.
<!-- Эти строки эквивалентны -->
<button disabled={disabled}>...</button>
<button {disabled}>...</button>
Значения, передаваемые в компоненты, называются свойствами или пропсами, но не атрибутами, поскольку они не относятся к DOM.
Как и в DOM-элементах, name={name}
можно заменить сокращением {name}
.
<Widget foo={bar} answer={42} text="hello"/>
Развёртка атрибутов позволяет передать компоненту сразу несколько атрибутов или свойств.
Элемент или компонент может иметь сразу несколько таких развёрток вперемешку с обычными атрибутами.
<Widget {...things}/>
Объект $$props
содержит в себе все свойства передаваемые компоненту, включая и те, которые не объявлены с помощью оператора export
. В редких случаях использование этого объекта может быть полезным, но в целом не рекомендуется его использовать, поскольку это вызовет определённые трудности у Svelte при оптимизации приложения.
<Widget {...$$props}/>
Объект $$restProps
содержит в себе только те переданные свойства, которые не были объявлены при помощи оператора export
. Он может быть использован для передачи заранее неизвестных атрибутов какому-либо элементу внутри компонента.
<input {...$$restProps}>
Атрибут
value
у элементаinput
, а также дочерние элементыoption
не могут быть установлены развёрткой атрибутов когда используются привязкиbind:group
илиbind:checked
. В такой ситуации Svelte необходимо видеть значения атрибутовvalue
непосредственно в шаблоне, чтобы связать его с соответствующей переменной.
{выражение}
Текст также может содержать JavaScript-выражения:
<h1>Привет, {name}!</h1>
<p>{a} + {b} = {a + b}.</p>
<div>{(/^[A-Za-z ]+$/).test(value) ? x : y}</div>
Вы можете использовать внутри компонентов обычные HTML-комментарии.
<!-- это комментарий! -->
<h1>Привет мир</h1>
Комментарии, которые начинаются со svelte-ignore
, отключают определённые предупреждения для следующего за ним блока разметки. Обычно это используется для предупреждения о доступности — убедитесь, что вы отключаете их по уважительной причине.
<!-- svelte-ignore a11y-autofocus -->
<input bind:value={name} autofocus>
{#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 выражение as имя}...{/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>
Можно выполнять перебор по массиву или массивоподобному значению, т.е. по любому объекту, у которого есть свойство length
.
Блок 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 выражение}...{:then имя}...{:catch имя}...{/await}
{#await выражение}...{:then имя}...{/await}
{#await выражение then имя}...{/await}
{#await выражение catch имя}...{/await}
Блоки await
позволяют обрабатывать три возможных состояния промисов — ожидание, выполнение или отклонение. В режиме SSR на сервере будет отображаться только состояние ожидания.
{#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}
Аналогично, если нужно показать только ошибку, можно опустить блок then
.
{#await promise catch error}
<p>Произошла ошибка: {error}</p>
{/await}
{#key выражение}...{/key}
Блок key
уничтожает и пересоздаёт свое содержимое всякий раз, когда значение выражения меняется.
Он полезен когда, вам нужно проиграть какой-либо переход при изменении значения переменной.
{#key value}
<div transition:fade>{value}</div>
{/key}
Когда внутри помещаются компоненты, они будут пересозданы заново.
{#key value}
<Component />
{/key}
{@html выражение}
В текстовых выражениях, символы вроде <
и >
экранируются, а в HTML выражениях — нет.
Выражение должно быть самодостаточной и валидной HTML-разметкой — {@html "<div>"}содержимое{@html "</div>"}
не сработает, поскольку </div>
не является валидной HTML-разметкой, и не скомпилирует код Svelte.
Svelte не очищает HTML код перед его обработкой! Если данные приходят из ненадёжного источника, необходимо их проверить. В противном случае, вы подвергаете пользователей возможным XSS-атакам.
<div class="blog-post">
<h1>{post.title}</h1>
{@html post.content}
</div>
{@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
, который будет срабатывать при любом изменении состояния.
{@const assignment}
Тег {@const ...}
определяет локальную константу.
<script>
export let boxes;
</script>
{#each boxes as box}
{@const area = box.width * box.height}
{box.width} * {box.height} = {area}
{/each}
{@const}
допускается только в качестве прямого дочернего элемента {#each}
, {:then}
, {:catch}
, <Component />
или <svelte:fragment />
.
Наряду с атрибутами у элементов могут быть и директивы, которые предоставляют некоторые дополнительные возможности.
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 добавит этот модификатор автоматически там, где это безопасно).nonpassive
— явно устанавливаетpassive: false
capture
— вызывает событие в режиме capture вместо bubbling.once
— удаляет обработчик события после первого вызова.self
— обработчик сработает, только если в свойствеevent.target
находится сам элемент, на котором слушается событиеtrusted
— обработчик сработает, только еслиevent.isTrusted
имеет значениеtrue
. Т.е. если событие инициируется действием пользователя.
Модификаторы можно соединять в цепочку, например 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:
позволяет передавать данные в другую сторону, от дочернего элемента в родительский. Большинство привязок соотносятся с конкретными элементами.
Простейшие привязки просто передают значение свойства элемента, например, 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}>
На элементах <input>
с атрибутом type="file"
можно использовать привязку bind:files
, чтобы получить список выбранных файлов в виде FileList
только для чтения.
<label for="avatar">Загрузить картинку:</label>
<input
accept="image/png, image/jpeg"
bind:files
id="avatar"
name="avatar"
type="file"
/>
bind:
может быть использован вместе с директивой on:
. Их порядок определяет значение связанной переменной при вызове обработчика событий.
<script>
let value = 'Hello World';
</script>
<input
on:input="{() => console.log('Old value:', value)}"
bind:value
on:input="{() => console.log('New value:', value)}"
/>
Привязка значения <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>
Элементы с атрибутом contenteditable
поддерживают привязки к innerHTML
и textContent
.
<div contenteditable="true" bind:innerHTML={html}></div>
Элемент <details>
поддерживает биндинг к атрибуту open
.
<details bind:open={isOpen}>
<summary>Details</summary>
<p>
Что-то достаточно маленькое, чтобы избежать случайного уведомления.
</p>
</details>
Медиа-элементы (<audio>
и <video>
) имеют свой собственный набор привязок — шесть только для чтения ...
duration
(только для чтения) — общая продолжительность, в секундахbuffered
(только для чтения) — буфер, массив объектов{start, end}
played
(только для чтения) — уже проиграно, то же самоеseekable
(только для чтения) — доступное для перемотки, то же самоеseeking
(только для чтения) — выполняется ли перемоткаended
(только для чтения) — окончилось ли воспроизведение
... и пять двусторонних привязок:
currentTime
— текущая позиция проигрывания, в секундахplaybackRate
— скорость проигрывания видео, где 1 обозначает 'нормально'paused
— остановлено проигрывание или нетvolume
— громкость, значение между 0 и 1muted
— логическое значение, указывающее приглушен ли звук
Для видео также доступны привязки для чтения ширины videoWidth
и высоты videoHeight
.
<video
src={clip}
bind:duration
bind:buffered
bind:seekable
bind:seeking
bind:played
bind:ended
bind:currentTime
bind:paused
bind:volume
bind:videoWidth
bind:videoHeight
></video>
У блочных элементов есть 4 привязки, доступных только для чтения. Они рассчитываются с использованием метода, аналогичного этому:
clientWidth
clientHeight
offsetWidth
offsetHeight
<div
bind:offsetWidth={width}
bind:offsetHeight={height}
>
<Chart {width} {height}/>
</div>
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={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:
обеспечивает простой способ управления классами элемента.
<!-- Эти строки эквивалентны -->
<div class="{active ? 'active' : ''}">...</div>
<div class:active={active}>...</div>
<!-- Сокращение, при одинаковых имени и значении -->
<div class:active>...</div>
<!-- можно использовать сразу несколько переключателей классов -->
<div class:active class:inactive={!active} class:isAdmin>...</div>
style:property={value}
style:property="value"
style:property
Директива style:
предоставляет сокращение для установки нескольких стилей на элементе.
<!--Это эквивалентно -->
<div style:color="red">...</div>
<div style="color: red;">...</div>
<!-- Переменные могут быть использованы -->
<div style:color={myColor}>...</div>
<!-- Сокращение для при имении свойства и переменной имени -->
<div style:color>...</div>
<!-- Многократные стили могут быть включены -->
<div style:color style:width="12rem" style:background-color={darkMode ? "black" : "white"}>...</div>
Когда директивы style:
объединяются с атрибутами style
, директивы будут иметь приоритет:
<div style="color: blue;" style:color="red">This will be red</div>
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|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 = 1 }) {
const valid = (
node.childNodes.length === 1 &&
node.childNodes[0].nodeType === Node.TEXT_NODE
);
if (!valid) {
throw new Error(`This transition only works on elements with a single text node child`);
}
const text = node.textContent;
const duration = text.length / (speed * 0.01);
return {
duration,
tick: t => {
const i = ~~(text.length * t);
node.textContent = text.slice(0, i);
}
};
}
</script>
{#if visible}
<p in:typewriter="{{ speed: 1 }}">
Съешь ещё этих мягких французских булок, да выпей же чаю
</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
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:имя={параметры}
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 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:событие={обработчик}
Компоненты могут отправлять события, используя диспетчер событий createEventDispatcher или пробрасывая события DOM. Обработка событий компонента выглядит так же, как обработка событий DOM.
<SomeComponent on:whatever={handler}/>
Если директива on:
используется без значения, то компонент будет пробрасывать событие выше, как и в аналогичном случае с событиями DOM. Событие станет доступно для прослушивания в родительском компоненте.
<SomeComponent on:whatever/>
--style-props="anycssvalue"
Также можно передавать стили в качестве реквизита компонентам для целей тематизации, используя пользовательские свойства CSS.
Реализация Svelte по сути является синтаксическим сахаром для добавления элемента обертки. Вот пример:
<Slider
bind:value
min={0}
--rail-color="black"
--track-color="rgb(0, 0, 255)"
/>
Без сахара:
<div style="display: contents; --rail-color: black; --track-color: rgb(0, 0, 255)">
<Slider
bind:value
min={0}
max={100}
/>
</div>
Примечание: Поскольку это дополнительный div, помните, что ваша структура CSS может случайно зацепиться за это. Помните об этом добавленом элементе обертки при использовании этой функции.
Поддержка CSS-переменных Svelte позволяет легко создавать темизируемые компоненты:
<!-- Slider.svelte -->
<style>
.potato-slider-rail {
background-color: var(--rail-color, var(--theme-color, 'purple'));
}
</style>
Таким образом, можно установить цвет темы :root:
/* global.css */
html {
--theme-color: black;
}
Или переопределить его на уровне потребителя:
<Slider --rail-color="goldenrod"/>
bind:свойство={переменная}
К свойствам компонента можно привязаться, точно так же как к атрибутам элементов.
<Keypad bind:value={pin}/>
bind:this={экземпляр_компонента}
Компоненты также поддерживают привязку bind:this
, позволяющую взаимодействовать с экземпляром компонента в коде.
Обратите внимание, что использование
{cart.empty}
вызовет ошибку, поскольку при первой отрисовке кнопкиcart
ещё имеет значениеundefined
.
<ShoppingCart bind:this={cart}/>
<button on:click={() => cart.empty()}>
Очистить корзину
</button>
<slot><!-- содержимое по умолчанию --></slot>
<slot name="x"><!-- содержимое по умолчанию --></slot>
<slot свойство={значениие}></slot>
Как и любой HTML-элемент, компоненты могут иметь вложенные элементы.
Вложенное содержимое размещается в компоненте при помощи элемента <slot>
, который также может содержать содержимое по умолчанию, которое отображается, если в компонент не было предано никакого содержимого.
<!-- Widget.svelte -->
<div>
<slot>
это содержимое по умолчанию, которое отобразится, если не передали иного содержимого.
</slot>
</div>
<!-- App.svelte -->
<Widget></Widget> <!-- этот компонент покажет содержимое по умолчанию -->
<Widget>
<p>вложенный элемент, который заменит собой содержимое по умолчанию.</p>
</Widget>
Именованные слоты позволяют указать конкретные области. Они тоже могут иметь запасное содержимое по умолчанию.
<!-- Widget.svelte -->
<div>
<slot name="header">Заголовок не задан</slot>
<p>Любое содержимое между заголовком и футером</p>
<slot name="footer"></slot>
</div>
<!-- App.svelte -->
<Widget>
<h1 slot="header">Привет</h1>
<p slot="footer">Все права (c) 2019 Svelte Industries</p>
</Widget>
Компоненты могут быть размещены в именованном слоте с использованием синтаксиса <Component slot="name" />
. Чтобы разместить контент в слоте без использования элемента-оболочки, вы можете использовать специальный элемент <svelte:fragment>
.
<!-- Widget.svelte -->
<div>
<slot name="header">Заголовок не задан</slot>
<p>Любое содержимое между заголовком и футером</p>
<slot name="footer"></slot>
</div>
<!-- App.svelte -->
<Widget>
<HeaderComponent slot="header" />
<svelte:fragment slot="footer">
<p>Все права защищены.</p>
<p>Копирайт (c) 2019 Svelte Industries</p>
</svelte:fragment>
</Widget>
$$slots
- это объект, ключи которого являются именами слотов, переданных в компонент родительским компонентом. Если родительский компонент не передавал содержимое для слота, то имя этого слота не будет присутствовать в $$slots
. Эту особенность можно использовать для отображения как самого слота, так и части шаблона в зависимости от того, передавал ли родительский компонент содержимое для этого слота.
Обратите внимание, что передача пустого элемента в слот все равно добавляет значение в $$slots
. Например, если родительский компонент передает <div slot ="title"/>
в дочерний, то $$slots.title
будет равно true
.
<!-- Card.svelte -->
<div>
<slot name="title"></slot>
{#if $$slots.description}
<!-- Элемент <hr> и слот будут отрисованы, только если будет получен слот с именем "description". -->
<hr>
<slot name="description"></slot>
{/if}
</div>
<!-- App.svelte -->
<Card>
<h1 slot="title">Заголовок статьи</h1>
<!-- Слота с именем "description" нет, поэтому ничего не будет отображено. -->
</Card>
Слоты могут быть отрисованы ноль или более раз и могут передавать значения обратно родителю через свои свойства. Родитель может получить эти значения от слота при помощи директивы let:
.
Обычные правила сокращения работают и тут — let:item
то же самое, что и let:item={item}
, а <slot {item}>
эквивалентно <slot item={item}>
.
<!-- FancyList.svelte -->
<ul>
{#each items as item}
<li class="fancy">
<slot prop={item}></slot>
</li>
{/each}
</ul>
<!-- App.svelte -->
<FancyList {items} let:prop={thing}>
<div>{thing.text}</div>
</FancyList>
Именованные слоты также могут предоставлять значения. Директива let:
указывается на элементе с атрибутом slot
.
<!-- FancyList.svelte -->
<ul>
{#each items as item}
<li class="fancy">
<slot name="item" item={item}></slot>
</li>
{/each}
</ul>
<slot name="footer"></slot>
<!-- App.svelte -->
<FancyList {items}>
<div slot="item" let:item={item}>{item.text}</div>
<p slot="footer">Все права (c) 2019 Svelte Industries</p>
</FancyList>
Элемент <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 this={выражение}/>
Элемент <svelte:component>
отрисовывает компонент динамически, используя конструктор компонента, переданный в свойствеthis
. Когда значение свойства изменяется, компонент уничтожается и создается заново.
Если выражение в свойстве this
ложное, компонент не отрисовывается.
<svelte:component this={currentSelection.component} foo={bar}/>
<svelte:window on:событие={обработчик}/>
<svelte:window bind:свойство={значение}/>
Элемент <svelte:window>
позволяет добавлять обработчики событий к объекту window
, не беспокоясь об их удалении при уничтожении компонента или проверять существование window
при рендеринге на стороне сервера.
В отличие от <svelte:self>
этот элемент может быть только на верхнем уровне вашего компонента и никогда не должен находиться внутри блока или элемента.
<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}/>
Обратите внимание, что страница не будет прокручиваться до начального значения, чтобы избежать проблем со специальными возможностями. Только последующие изменения связанной переменной
scrollX
иscrollY
вызовут прокрутку. Однако, если требуется поведение прокрутки, вызовитеscrollTo()
вonMount()
.
<svelte:body on:событие={обработчик}/>
Подобно <svelte:window>
, этот элемент позволяет добавлять обработчики событий на document.body
, такие как mouseenter
и mouseleave
, которые не запускаются на window
. Он также позволяет использовать actions для элемента <body>
.
<svelte:body>
также должен находиться на верхнем уровне вашего компонента.
<svelte:body
on:mouseenter={handleMouseenter}
on:mouseleave={handleMouseleave}
use:someAction
/>
<svelte:head>...</svelte:head>
Этот элемент позволяет вставлять элементы в document.head
. При рендеринге на стороне сервера содержимое head
предоставляется отдельно от основного содержимого html
.
Как и в случае со <svelte:window
и <svelte:body
, этот элемент должен быть на верхнем уровне вашего компонента и не может находиться внутри блока или другого элемента.
<svelte:head>
<link rel="stylesheet" href="/tutorial/dark-theme.css">
</svelte:head>
<svelte:options параметр={значение}/>
Элемент <svelte:options>
позволяет установить определенные параметры для компилятора для каждого отдельного компонента. Подробнее о параметрах компилятора можно узнать в разделе про функцию compile. Параметры, которые можно установить:
immutable={true}
— установите, если вы нигде не используете изменяемые данные — тогда компилятор сможет выполнять более простые проверки равенства объектов для определения их измененияimmutable={false}
— по умолчанию. Svelte будет проверять изменение объектов обычным способомaccessors={true}
— добавляет сеттеры и геттеры для свойств компонентаaccessors={false}
— по умолчанию, аксессоры не добавляютсяnamespace="..."
— пространство имен, где компонент будет использован (обычно требуется "svg"); используйте "foreign" чтобы имена аттрибутов стали чувствительны к регистру и не отображались ошибки специфичные для HTMLtag="..."
— имя, которое используется при компиляции компонента в пользовательский элемент
<svelte:options tag="my-custom-element"/>
Элемент <svelte:fragment>
позволяет размещать контент в именованных слотах не оборачивая его в дополнительный элемент DOM. Это сохраняет структуру макета вашего документа.
<!-- Widget.svelte -->
<div>
<slot name="header">Заголовок не предоставлен</slot>
<p>Любое содержимое между заголовком и футером</p>
<slot name="footer"></slot>
</div>
<!-- App.svelte -->
<Widget>
<h1 slot="header">Привет</h1>
<svelte:fragment slot="footer">
<p>Все права защищены.</p>
<p>Копирайт (c) 2019 Svelte Industries</p>
</svelte:fragment>
</Widget>