综合实战示例
本章把前面学到的 Runes 与组件知识串起来,通过几个可交互的完整示例巩固理解。每个卡片都是真实运行的 Svelte 5.57 组件。
示例一:计数器($state + 事件)
最经典的入门示例。展示 $state 声明状态、onclick 处理事件。
计数器 · $stateSvelte 5 · Live
涉及的知识点:
$state(0)声明响应式计数onclick={fn}绑定点击处理- 函数内直接
count += 1修改状态
svelte
<script>
let count = $state(0);
function increment() { count += 1; }
</script>
<button onclick={increment}>+ {count}</button>示例二:几何计算器($derived 派生)
拖动滑块改变长宽,面积、周长、形状判断全部由 $derived 实时计算。
派生状态 · $derivedSvelte 5 · Live
涉及的知识点:
bind:value双向绑定滑块到状态$derived(expr)计算派生值- 派生值依赖多个状态,任一变化都会重算
style:width/style:height动态内联样式- 三元表达式结合
class:指令
svelte
<script>
let width = $state(10);
let height = $state(5);
const area = $derived(width * height);
const perimeter = $derived(2 * (width + height));
const isSquare = $derived(width === height);
</script>
<input type="range" min="1" max="20" bind:value={width} />
<div style:width="{width * 12}px" style:height="{height * 12}px"></div>打开控制台
这个例子没有用 $effect,因为「计算值」应该用 $derived 而非副作用 —— 这正是我们强调的正确用法。
示例三:主题切换($props + $bindable)
一个用 $props 声明属性、用 $bindable 支持双向绑定的开关组件。
主题切换 · $props / $bindableSvelte 5 · Live
涉及的知识点:
let { theme = $bindable('light'), label = '主题' } = $props()- 带默认值的普通 prop(
label) - 可双向绑定的 prop(
theme)
svelte
<script>
let { theme = $bindable('light'), label = '主题' } = $props();
function toggle() {
theme = theme === 'light' ? 'dark' : 'light';
}
</script>
<button onclick={toggle}>{theme === 'light' ? '🌞' : '🌙'} {label}</button>示例四:音量控制(父子双向绑定)
父组件持有 volume 状态,通过 bind:volume 与子组件双向同步。拖动滑块或点静音,两边同时更新。
双向绑定 · $bindableSvelte 5 · Live
涉及的知识点:
- 父组件
let volume = $state(50) <VolumeControl bind:volume />建立双向绑定- 子组件
$bindableprop 内部也能改 - 父组件用
$derived判断是否静音
svelte
<!-- 父组件 -->
<script>
import VolumeControl from './VolumeControl.svelte';
let volume = $state(50);
let muted = $derived(volume === 0);
</script>
<VolumeControl bind:volume />
<button onclick={() => (volume = muted ? 50 : 0)}>
{muted ? '取消静音' : '静音'}
</button>示例五:卡片内容分发(Snippets)
用 {#snippet} 定义具名片段、{@render} 渲染,实现灵活的组件组合。footer 还能读到父组件的实时状态。
Snippets · {@render}Svelte 5 · Live
涉及的知识点:
{@render children()}渲染默认内容{#snippet header()}/{#snippet footer()}具名片段{#if header}条件渲染可选片段- 片段内可访问父组件状态(
likes)
svelte
<!-- 子组件 Card.svelte -->
<script>
let { children, header, footer } = $props();
</script>
<div class="card">
{#if header}<div class="card__header">{@render header()}</div>{/if}
<div class="card__body">{@render children()}</div>
{#if footer}<div class="card__footer">{@render footer()}</div>{/if}
</div>示例六:组件事件(回调即属性)
子组件通过回调 props「派发事件」,父组件记录日志。这是 Svelte 5 取代 createEventDispatcher 的核心模式。
组件事件 · 回调即属性Svelte 5 · Live
涉及的知识点:
- 子组件
let { onnotify, onreset } = $props() - 调用回调 = 派发事件
- 父组件传入处理函数
- 父组件用数组
$state维护日志
svelte
<!-- 子组件 -->
<script>
let { onnotify = () => {} } = $props();
let clicks = $state(0);
function handleClick() {
onnotify(`被点击了 ${++clicks} 次`);
}
</script>
<button onclick={handleClick}>点我触发事件</button>示例七:完整待办应用(综合运用)
一个功能完整的待办清单,几乎用到了所有核心特性。强烈建议打开浏览器控制台,观察 $effect 打印的日志。
待办清单 · 综合实战Svelte 5 · Live
涉及的知识点(综合):
$state管理数组、字符串、筛选条件等多个状态$derived计算剩余数量、过滤可见项$effect把状态同步到控制台(副作用)- 数组方法
push/filter直接修改(深度响应式) {#each ... (todo.id)}带 key 的列表渲染 +{:else}空状态bind:value/bind:checked表单双向绑定onsubmit+e.preventDefault()表单提交class:done={...}/class:active={...}条件类名
svelte
<script>
let todos = $state([/* ... */]);
let draft = $state('');
let filter = $state('all');
let nextId = 4;
const remaining = $derived(todos.filter((t) => !t.done).length);
const visible = $derived(
filter === 'active' ? todos.filter((t) => !t.done)
: filter === 'done' ? todos.filter((t) => t.done)
: todos
);
$effect(() => {
console.log(`[Todo demo] 剩余 ${remaining} 项,共 ${todos.length} 项`);
});
function addTodo() {
const text = draft.trim();
if (!text) return;
todos.push({ id: nextId++, text, done: false });
draft = '';
}
</script>
<form onsubmit={(e) => { e.preventDefault(); addTodo(); }}>
<input bind:value={draft} placeholder="添加任务…" />
<button type="submit">添加</button>
</form>
{#each visible as todo (todo.id)}
<label>
<input type="checkbox" bind:checked={todo.done} />
<span class:done={todo.done}>{todo.text}</span>
</label>
<button onclick={() => (todos = todos.filter((t) => t.id !== todo.id))}>×</button>
{:else}
<p>没有匹配的任务 🎉</p>
{/each}把这些搬进你的项目
本站所有示例组件都是纯 Svelte 5,可以直接复制到你的项目里:
- 把
.svelte文件放进src/lib/ - 在需要的地方
import并使用 - 用
mount()或在 SvelteKit 页面中直接引用
源码位置
本教程的示例组件源码位于项目 docs/demos/ 目录,你可以对照学习每个 .svelte 文件的完整实现。
下一步
- 系统查阅语法:Runes API 速查
- 从旧版本升级:从 Svelte 4 迁移
- 答疑解惑:常见问题