Хуки жизненного цикла
В Svelte 5 жизненный цикл компонента состоит только из двух частей: его создания и уничтожения. Всё, что происходит между ними — например, обновление определённого состояния — не связано с компонентом в целом; уведомляются только те части, которые должны реагировать на изменение состояния. Это связано с тем, что под капотом наименьшая единица изменения — это не компонент, а (рендер) эффекты, которые компонент настраивает при инициализации. Следовательно, таких понятий, как хуки beforeUpdate
/ afterUpdate
, не существует.
onMount
Функция onMount
планирует выполнение колбэка сразу после того, как компонент будет смонтирован в DOM. Она должна вызываться во время инициализации компонента (но не обязательно находиться внутри компонента; она может быть вызвана из внешнего модуля).
onMount
не выполняется внутри компонента, который рендерится на сервере.
<script>
import { onMount } from 'svelte';
onMount(() => {
console.log('компонент смонтирован');
});
</script>
Если из onMount
возвращается функция, она будет вызвана при размонтировании компонента.
<script>
import { onMount } from 'svelte';
onMount(() => {
const interval = setInterval(() => {
console.log('beep');
}, 1000);
return () => clearInterval(interval);
});
</script>
onDestroy
Планирует выполнение колбэка непосредственно перед размонтированием компонента.
Из onMount
, beforeUpdate
, afterUpdate
и onDestroy
это единственный хук, который выполняется внутри компонента на стороне сервера.
<script>
import { onDestroy } from 'svelte';
onDestroy(() => {
console.log('компонент уничтожен');
});
</script>
tick
Хотя хука afterUpdate
не существует, вы можете использовать tick
, чтобы убедиться, что пользовательский интерфейс обновлён перед продолжением. tick
возвращает Promise
, который выполняется после применения всех ожидающих изменений состояния или в следующей микрозадаче, если изменений нет.
<script>
import { tick } from 'svelte';
$effect.pre(() => {
console.log('компонент собирается обновиться');
tick().then(() => {
console.log('компонент только что обновлён');
});
});
</script>
Устарело: beforeUpdate
/ afterUpdate
В Svelte 4 были хуки, которые выполнялись до и после обновления компонента в целом. Для обратной совместимости эти хуки были добавлены в Svelte 5, но они недоступны внутри компонентов, использующих руны.
<script>
import { beforeUpdate, afterUpdate } from 'svelte';
beforeUpdate(() => {
console.log('компонент собирается обновиться');
});
afterUpdate(() => {
console.log('компонент только что обновлён');
});
</script>
Вместо beforeUpdate
используйте $effect.pre
, а вместо afterUpdate
— $effect
. Эти руны предоставляют более детальный контроль и реагируют только на те изменения, которые вас действительно интересуют.
Пример окна чата
Чтобы реализовать окно чата, которое автоматически прокручивается вниз при появлении новых сообщений (но только если вы уже прокрутили окно вниз), нам нужно измерить DOM перед его обновлением.
В Svelte 4 мы делаем это с помощью beforeUpdate
, но этот подход имеет недостатки — он срабатывает перед каждым обновлением, независимо от того, актуально оно или нет. В примере ниже нам нужно ввести проверки, такие как updatingMessages
, чтобы убедиться, что мы не изменяем положение прокрутки при переключении тёмного режима.
С рунами мы можем использовать $effect.pre
, который ведет себя так же, как $effect
, но выполняется до обновления DOM. Если мы явно ссылаемся на messages
внутри тела эффекта, он будет запускаться при каждом изменении messages
, но не при изменении theme
.
Таким образом, beforeUpdate
и его не менее проблемный аналог afterUpdate
устарели в Svelte 5.
<script>
- import { beforeUpdate, afterUpdate, tick } from 'svelte';
+ import { tick } from 'svelte';
- let updatingMessages = false;
- let theme = 'dark';
- let messages = [];
+ let theme = $state('dark');
+ let messages = $state([]);
let viewport;
- beforeUpdate(() => {
+ $effect.pre(() => {
- if (!updatingMessages) return;
+ messages;
const autoscroll = viewport && viewport.offsetHeight + viewport.scrollTop > viewport.scrollHeight - 50;
if (autoscroll) {
tick().then(() => {
viewport.scrollTo(0, viewport.scrollHeight);
});
}
- updatingMessages = false;
});
function handleKeydown(event) {
if (event.key === 'Enter') {
const text = event.target.value;
if (!text) return;
- updatingMessages = true;
messages = [...messages, text];
event.target.value = '';
}
}
function toggle() {
toggleValue = !toggleValue;
}
</script>
<div class:dark={theme === 'dark'}>
<div bind:this={viewport}>
{#each messages as message}
<p>{message}</p>
{/each}
</div>
- <input on:keydown={handleKeydown} />
+ <input onkeydown={handleKeydown} />
- <button on:click={toggle}> Переключить тёмный режим </button>
+ <button onclick={toggle}> Переключить тёмный режим </button>
</div>