生命周期钩子
在 Svelte 5 中,组件生命周期仅包含两个部分:它的创建和它的销毁。介于两者之间的一切——当某些状态更新时——与整个组件无关;只有需要对状态更改做出反应的部分才会收到通知。这是因为在幕后,更改的最小单位实际上不是组件,而是组件在组件初始化时设置的(渲染)效果。因此,不存在“before update”/“after update”钩子。
onMount
onMount
函数安排一个回调函数,在组件已挂载到 DOM 后立即运行。它必须在组件的初始化期间调用(但不需要位于组件内部;它可以从外部模块调用)。
onMount
不会在服务器端渲染的组件内部运行。
<script>
import { onMount } from 'svelte';
onMount(() => {
console.log('the component has mounted');
});
</script>
如果从 onMount
返回一个函数,则在组件卸载时将调用该函数。
<script>
import { onMount } from 'svelte';
onMount(() => {
const interval = setInterval(() => {
console.log('beep');
}, 1000);
return () => clearInterval(interval);
});
</script>
此行为仅在传递给
onMount
的函数同步返回值时有效。async
函数始终返回一个Promise
,因此无法同步返回函数。
onDestroy
安排一个回调函数,在组件卸载之前立即运行。
在 onMount
、beforeUpdate
、afterUpdate
和 onDestroy
中,这是唯一一个在服务器端组件内部运行的钩子。
function onDestroy(fn: () => any): void;
安排一个回调函数,在组件卸载之前立即运行。
在 onMount
、beforeUpdate
、afterUpdate
和 onDestroy
中,这是唯一一个在服务器端组件内部运行的钩子。
<script>
import { onDestroy } from 'svelte';
onDestroy(() => {
console.log('the component is being destroyed');
});
</script>
tick
虽然没有“after update”钩子,但可以使用 tick
来确保在继续之前 UI 已更新。tick
返回一个 promise,在任何挂起的状态更改都已应用后或如果没有挂起的更改则在下一个微任务中解析。
<script>
import { tick } from 'svelte';
$effect.pre(() => {
console.log('the component is about to update');
tick().then(() => {
console.log('the component just updated');
});
});
</script>
已弃用:beforeUpdate / afterUpdate
Svelte 4 包含在整个组件更新之前和之后运行的钩子。为了向后兼容,这些钩子在 Svelte 5 中进行了模拟,但在使用符文的组件内部不可用。
<script>
import { beforeUpdate, afterUpdate } from 'svelte';
beforeUpdate(() => {
console.log('the component is about to update');
});
afterUpdate(() => {
console.log('the component just updated');
});
</script>
不要使用 beforeUpdate
,请改用 $effect.pre
;不要使用 afterUpdate
,请改用 $effect
——这些符文提供了更细粒度的控制,并且只对您真正感兴趣的更改做出反应。
聊天窗口示例
要实现一个聊天窗口,当出现新消息时自动滚动到底部(但仅当您已经滚动到底部时),我们需要在更新 DOM 之前测量 DOM。
在 Svelte 4 中,我们使用 beforeUpdate
来实现这一点,但这是一种有缺陷的方法——它在每次更新之前都会触发,无论它是否相关。在下面的示例中,我们需要引入诸如 updatingMessages
之类的检查,以确保在有人切换深色模式时我们不会弄乱滚动位置。
使用符文,我们可以使用 $effect.pre
,它的行为与 $effect
相同,但在 DOM 更新之前运行。只要我们在 effect 主体中明确引用 messages
,它就会在 messages
更改时运行,但在 theme
更改时不会运行。
因此,beforeUpdate
及其同样麻烦的对应项 afterUpdate
在 Svelte 5 中已弃用。
<script>
import { beforeUpdate, afterUpdate, tick } from 'svelte';
let updatingMessages = false;
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 onkeydown={handleKeydown} />
<button onclick={toggle}> Toggle dark mode </button>
</div>