Skip to main content

生命周期钩子

在 Svelte 5 中,组件生命周期仅由两部分组成:它的创建和销毁。当某些状态更新时,中间的所有内容都与整个组件无关;仅通知需要对状态更改做出反应的部分。这是因为在底层,最小的变化单位实际上不是组件,而是组件在初始化时设置的(渲染)效果。因此,没有 “更新前”/"更新后” 钩子之类的东西。

¥In Svelte 5, the component lifecycle consists of only two parts: Its creation and its destruction. Everything in-between — when certain state is updated — is not related to the component as a whole; only the parts that need to react to the state change are notified. This is because under the hood the smallest unit of change is actually not a component, it’s the (render) effects that the component sets up upon component initialization. Consequently, there’s no such thing as a “before update”/"after update” hook.

onMount

onMount 函数安排回调在组件安装到 DOM 后立即运行。它必须在组件初始化期间调用(但不需要位于组件内部;可以从外部模块调用它)。

¥The onMount function schedules a callback to run as soon as the component has been mounted to the DOM. It must be called during the component’s initialisation (but doesn’t need to live inside the component; it can be called from an external module).

onMount 不在服务器上渲染的组件内运行。

¥onMount does not run inside a component that is rendered on the server.

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

	onMount(() => {
		console.log('the component has mounted');
	});
</script>

如果从 onMount 返回一个函数,则在卸载组件时将调用该函数。

¥If a function is returned from onMount, it will be called when the component is unmounted.

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

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

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

仅当传递给 onMount 的函数同步返回值时,此行为才会起作用。async 函数始终返回 Promise,因此无法同步返回函数。

¥[!NOTE] This behaviour will only work when the function passed to onMount synchronously returns a value. async functions always return a Promise, and as such cannot synchronously return a function.

onDestroy

安排回调在组件卸载之前立即运行。

¥Schedules a callback to run immediately before the component is unmounted.

onMountbeforeUpdateafterUpdateonDestroy 中,这是唯一在服务器端组件内运行的。

¥Out of onMount, beforeUpdate, afterUpdate and onDestroy, this is the only one that runs inside a server-side component.

function onDestroy(fn: () => any): void;

安排回调在组件卸载之前立即运行。

¥Schedules a callback to run immediately before the component is unmounted.

onMountbeforeUpdateafterUpdateonDestroy 中,这是唯一在服务器端组件内运行的。

¥Out of onMount, beforeUpdate, afterUpdate and onDestroy, this is the only one that runs inside a server-side component.

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

	onDestroy(() => {
		console.log('the component is being destroyed');
	});
</script>

tick

虽然没有 “更新后” 钩子,但你可以使用 tick 确保在继续之前更新 UI。tick 返回一个 promise,一旦应用了任何待处理的状态更改,该 promise 就会解析,如果没有,则在下一个微任务中解析。

¥While there’s no “after update” hook, you can use tick to ensure that the UI is updated before continuing. tick returns a promise that resolves once any pending state changes have been applied, or in the next microtask if there are none.

<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(Deprecated: beforeUpdate / afterUpdate)

¥Deprecated: beforeUpdate / afterUpdate

Svelte 4 包含在组件整体更新之前和之后运行的钩子。为了向后兼容,这些钩子在 Svelte 5 中被填充,但在使用符文的组件内部不可用。

¥Svelte 4 contained hooks that ran before and after the component as a whole was updated. For backwards compatibility, these hooks were shimmed in Svelte 5 but not available inside components that use runes.

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

	beforeUpdate(() => {
		console.log('the component is about to update');
	});

	afterUpdate(() => {
		console.log('the component just updated');
	});
</script>

使用 $effect.pre 代替 beforeUpdate,使用 $effect 代替 afterUpdate - 这些符文提供更精细的控制,并且只对你真正感兴趣的更改做出反应。

¥Instead of beforeUpdate use $effect.pre and instead of afterUpdate use $effect instead - these runes offer more granular control and only react to the changes you’re actually interested in.

聊天窗口示例(Chat window example)

¥Chat window example

要实现一个聊天窗口,当出现新消息时自动滚动到底部(但前提是你已经滚动到底部),我们需要在更新 DOM 之前测量它。

¥To implement a chat window that autoscrolls to the bottom when new messages appear (but only if you were already scrolled to the bottom), we need to measure the DOM before we update it.

在 Svelte 4 中,我们使用 beforeUpdate 来做到这一点,但这是一种有缺陷的方法 - 它会在每次更新之前触发,无论是否相关。在下面的例子中,我们需要引入像 updatingMessages 这样的检查,以确保当有人切换夜间模式时,我们不会弄乱滚动位置。

¥In Svelte 4, we do this with beforeUpdate, but this is a flawed approach — it fires before every update, whether it’s relevant or not. In the example below, we need to introduce checks like updatingMessages to make sure we don’t mess with the scroll position when someone toggles dark mode.

使用 runes,我们可以使用 $effect.pre,它的行为与 $effect 相同,但在 DOM 更新之前运行。只要我们在效果主体内明确引用 messages,它就会在 messages 更改时运行,但不会当 theme 更改时运行。

¥With runes, we can use $effect.pre, which behaves the same as $effect but runs before the DOM is updated. As long as we explicitly reference messages inside the effect body, it will run whenever messages changes, but not when theme changes.

因此,beforeUpdate 及其同样麻烦的对应物 afterUpdate 在 Svelte 5 中已弃用。

¥beforeUpdate, and its equally troublesome counterpart afterUpdate, are therefore deprecated in 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>
上一页 下一页