组件生命周期 #
一、生命周期概述 #
Svelte 组件从创建到销毁经历不同的阶段,每个阶段都有对应的生命周期函数。
text
┌─────────────────────────────────────────────────────────────┐
│ Svelte 组件生命周期 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 创建阶段 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ onMount ──→ 组件挂载到DOM后执行 │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ │
│ 更新阶段 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ beforeUpdate ──→ DOM更新前执行 │ │
│ │ afterUpdate ──→ DOM更新后执行 │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ │
│ 销毁阶段 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ onDestroy ──→ 组件销毁前执行 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
二、onMount #
2.1 基本用法 #
svelte
<script>
import { onMount } from 'svelte';
onMount(() => {
console.log('Component mounted');
return () => {
console.log('Cleanup on unmount');
};
});
</script>
<p>Component content</p>
2.2 DOM 操作 #
svelte
<script>
import { onMount } from 'svelte';
let canvas;
let ctx;
onMount(() => {
ctx = canvas.getContext('2d');
ctx.fillStyle = 'red';
ctx.fillRect(10, 10, 50, 50);
});
</script>
<canvas bind:this={canvas} width="200" height="200"></canvas>
2.3 数据获取 #
svelte
<script>
import { onMount } from 'svelte';
let data = [];
let loading = true;
let error = null;
onMount(async () => {
try {
const response = await fetch('/api/data');
data = await response.json();
} catch (e) {
error = e.message;
} finally {
loading = false;
}
});
</script>
{#if loading}
<p>Loading...</p>
{:else if error}
<p>Error: {error}</p>
{:else}
<ul>
{#each data as item}
<li>{item.name}</li>
{/each}
</ul>
{/if}
2.4 清理函数 #
svelte
<script>
import { onMount } from 'svelte';
let count = 0;
let interval;
onMount(() => {
interval = setInterval(() => {
count += 1;
}, 1000);
return () => {
clearInterval(interval);
console.log('Interval cleared');
};
});
</script>
<p>Count: {count}</p>
2.5 Svelte 5 $effect #
svelte
<script>
let count = $state(0);
$effect(() => {
const interval = setInterval(() => {
count += 1;
}, 1000);
return () => {
clearInterval(interval);
};
});
</script>
<p>Count: {count}</p>
三、onDestroy #
3.1 基本用法 #
svelte
<script>
import { onDestroy } from 'svelte';
let timer = setInterval(() => {
console.log('tick');
}, 1000);
onDestroy(() => {
clearInterval(timer);
console.log('Component destroyed');
});
</script>
3.2 清理订阅 #
svelte
<script>
import { onDestroy } from 'svelte';
let socket;
function connect() {
socket = new WebSocket('wss://example.com');
socket.onopen = () => {
console.log('Connected');
};
socket.onmessage = (event) => {
console.log('Message:', event.data);
};
}
connect();
onDestroy(() => {
if (socket) {
socket.close();
console.log('WebSocket closed');
}
});
</script>
3.3 清理事件监听 #
svelte
<script>
import { onDestroy } from 'svelte';
function handleResize() {
console.log('Window resized');
}
window.addEventListener('resize', handleResize);
onDestroy(() => {
window.removeEventListener('resize', handleResize);
});
</script>
四、beforeUpdate 和 afterUpdate #
4.1 beforeUpdate #
svelte
<script>
import { beforeUpdate } from 'svelte';
let count = 0;
beforeUpdate(() => {
console.log('Before update, count:', count);
});
</script>
<button onclick={() => count += 1}>
Count: {count}
</button>
4.2 afterUpdate #
svelte
<script>
import { afterUpdate } from 'svelte';
let messages = [];
let chatContainer;
afterUpdate(() => {
if (chatContainer) {
chatContainer.scrollTop = chatContainer.scrollHeight;
}
});
function addMessage(text) {
messages = [...messages, { text, time: new Date() }];
}
</script>
<div class="chat" bind:this={chatContainer}>
{#each messages as message}
<p>{message.text}</p>
{/each}
</div>
<button onclick={() => addMessage('New message')}>
Add Message
</button>
4.3 组合使用 #
svelte
<script>
import { beforeUpdate, afterUpdate } from 'svelte';
let count = 0;
let previousCount = 0;
beforeUpdate(() => {
previousCount = count;
});
afterUpdate(() => {
console.log(`Count changed from ${previousCount} to ${count}`);
});
</script>
<button onclick={() => count += 1}>
Count: {count}
</button>
五、tick #
5.1 基本用法 #
tick 函数返回一个 Promise,在下一个 DOM 更新周期后 resolve。
svelte
<script>
import { tick } from 'svelte';
let text = '';
let input;
async function handleClick() {
text = 'Updated';
await tick();
input.focus();
input.select();
}
</script>
<input bind:this={input} bind:value={text} />
<button onclick={handleClick}>Update and Focus</button>
5.2 等待 DOM 更新 #
svelte
<script>
import { tick } from 'svelte';
let items = [];
let listRef;
async function addItem() {
items = [...items, `Item ${items.length + 1}`];
await tick();
listRef.lastElementChild?.scrollIntoView({ behavior: 'smooth' });
}
</script>
<ul bind:this={listRef}>
{#each items as item}
<li>{item}</li>
{/each}
</ul>
<button onclick={addItem}>Add Item</button>
5.3 批量更新 #
svelte
<script>
import { tick } from 'svelte';
let a = 0;
let b = 0;
let c = 0;
async function updateAll() {
a = 1;
b = 2;
c = 3;
await tick();
console.log('All updates applied');
}
</script>
<p>a={a}, b={b}, c={c}</p>
<button onclick={updateAll}>Update All</button>
六、Svelte 5 生命周期 #
6.1 $effect 替代生命周期 #
svelte
<script>
let count = $state(0);
$effect(() => {
console.log('Component mounted or count changed:', count);
return () => {
console.log('Cleanup');
};
});
</script>
<button onclick={() => count++}>
Count: {count}
</button>
6.2 仅挂载时执行 #
svelte
<script>
let mounted = false;
$effect(() => {
if (!mounted) {
mounted = true;
console.log('Mounted once');
}
});
</script>
6.3 $effect.pre #
svelte
<script>
let items = $state([]);
$effect.pre(() => {
console.log('Before DOM update, items:', items.length);
});
</script>
七、实际应用示例 #
7.1 计时器组件 #
svelte
<script>
import { onMount, onDestroy } from 'svelte';
let {
duration = 60,
onTimeUp,
onPause,
onResume
} = $props();
let timeLeft = $state(duration);
let isRunning = $state(false);
let interval;
onMount(() => {
start();
});
onDestroy(() => {
stop();
});
function start() {
if (isRunning) return;
isRunning = true;
interval = setInterval(() => {
timeLeft -= 1;
if (timeLeft <= 0) {
stop();
onTimeUp?.();
}
}, 1000);
onResume?.();
}
function stop() {
if (!isRunning) return;
isRunning = false;
clearInterval(interval);
onPause?.();
}
function reset() {
stop();
timeLeft = duration;
}
let minutes = $derived(Math.floor(timeLeft / 60));
let seconds = $derived(timeLeft % 60);
</script>
<div class="timer">
<span class="time">
{String(minutes).padStart(2, '0')}:{String(seconds).padStart(2, '0')}
</span>
<div class="controls">
{#if isRunning}
<button onclick={stop}>Pause</button>
{:else}
<button onclick={start}>Resume</button>
{/if}
<button onclick={reset}>Reset</button>
</div>
</div>
<style>
.timer {
text-align: center;
padding: 2rem;
}
.time {
font-size: 3rem;
font-family: monospace;
}
.controls {
margin-top: 1rem;
display: flex;
gap: 0.5rem;
justify-content: center;
}
</style>
7.2 WebSocket 连接组件 #
svelte
<script>
import { onMount, onDestroy } from 'svelte';
let {
url,
onMessage,
onOpen,
onClose,
onError
} = $props();
let socket = null;
let connected = $state(false);
let messages = $state([]);
onMount(() => {
connect();
});
onDestroy(() => {
disconnect();
});
function connect() {
socket = new WebSocket(url);
socket.onopen = () => {
connected = true;
onOpen?.();
};
socket.onmessage = (event) => {
const data = JSON.parse(event.data);
messages = [...messages, data];
onMessage?.(data);
};
socket.onclose = () => {
connected = false;
onClose?.();
};
socket.onerror = (error) => {
onError?.(error);
};
}
function disconnect() {
if (socket) {
socket.close();
socket = null;
}
}
function send(data) {
if (socket && connected) {
socket.send(JSON.stringify(data));
}
}
</script>
<div class="websocket-status" class:connected class:disconnected={!connected}>
{connected ? '🟢 Connected' : '🔴 Disconnected'}
</div>
<slot {send} {messages} {connected} />
7.3 本地存储同步 #
svelte
<script>
import { onMount } from 'svelte';
let {
key,
defaultValue = null
} = $props();
let value = $state(defaultValue);
let loaded = $state(false);
onMount(() => {
const stored = localStorage.getItem(key);
if (stored !== null) {
try {
value = JSON.parse(stored);
} catch {
value = stored;
}
}
loaded = true;
});
$effect(() => {
if (loaded) {
localStorage.setItem(key, JSON.stringify(value));
}
});
</script>
{#if loaded}
<slot bind:value />
{/if}
八、生命周期对比 #
8.1 Svelte 4 vs Svelte 5 #
| Svelte 4 | Svelte 5 |
|---|---|
onMount |
$effect(首次执行时) |
onDestroy |
$effect 返回的清理函数 |
beforeUpdate |
$effect.pre |
afterUpdate |
$effect(每次状态变化后) |
tick |
tick(保持不变) |
8.2 迁移示例 #
Svelte 4:
svelte
<script>
import { onMount, onDestroy } from 'svelte';
let count = 0;
let interval;
onMount(() => {
interval = setInterval(() => count++, 1000);
});
onDestroy(() => {
clearInterval(interval);
});
</script>
Svelte 5:
svelte
<script>
let count = $state(0);
$effect(() => {
const interval = setInterval(() => count++, 1000);
return () => clearInterval(interval);
});
</script>
九、最佳实践 #
9.1 清理资源 #
svelte
<script>
import { onMount } from 'svelte';
onMount(() => {
const controller = new AbortController();
fetch('/api/data', { signal: controller.signal })
.then(r => r.json())
.then(data => {
console.log(data);
});
return () => {
controller.abort();
};
});
</script>
9.2 避免内存泄漏 #
svelte
<script>
import { onMount, onDestroy } from 'svelte';
let subscriptions = [];
onMount(() => {
subscriptions.push(
eventBus.subscribe('event1', handler1),
eventBus.subscribe('event2', handler2)
);
});
onDestroy(() => {
subscriptions.forEach(unsub => unsub());
});
</script>
十、总结 #
| 函数 | 说明 | Svelte 5 替代 |
|---|---|---|
onMount |
挂载后执行 | $effect |
onDestroy |
销毁前执行 | $effect 返回函数 |
beforeUpdate |
更新前执行 | $effect.pre |
afterUpdate |
更新后执行 | $effect |
tick |
等待DOM更新 | tick |
生命周期要点:
onMount用于初始化和 DOM 操作- 返回清理函数在组件销毁时执行
- Svelte 5 使用
$effect统一处理 - 使用
tick等待 DOM 更新 - 及时清理定时器、订阅等资源
最后更新:2026-03-28