Svelte组件结构 #
一、组件概述 #
Svelte 组件是构建应用的基本单元,每个组件封装了自己的逻辑、模板和样式。组件以 .svelte 文件形式存在。
1.1 单文件组件 #
text
┌─────────────────────────────────────────────────────────────┐
│ Component.svelte │
├─────────────────────────────────────────────────────────────┤
│ <script> │
│ // JavaScript 逻辑 │
│ // 状态、方法、导入导出 │
│ </script> │
├─────────────────────────────────────────────────────────────┤
│ <!-- HTML 模板 --> │
│ <div> │
│ 标记语言,绑定数据 │
│ </div> │
├─────────────────────────────────────────────────────────────┤
│ <style> │
│ /* CSS 样式 */ │
│ /* 自动作用域隔离 */ │
│ </style> │
└─────────────────────────────────────────────────────────────┘
二、Script 部分 #
2.1 基本结构 #
svelte
<script>
let name = 'Svelte';
let count = 0;
function increment() {
count += 1;
}
</script>
2.2 导入模块 #
svelte
<script>
import OtherComponent from './OtherComponent.svelte';
import { someFunction } from './utils.js';
import { fade } from 'svelte/transition';
import { writable } from 'svelte/store';
</script>
2.3 导出属性(Props) #
Svelte 4 语法:
svelte
<script>
export let name = 'World';
export let count = 0;
export let items = [];
</script>
Svelte 5 Runes 语法:
svelte
<script>
let { name = 'World', count = 0, items = [] } = $props();
</script>
2.4 响应式声明 #
Svelte 4 语法:
svelte
<script>
let count = 0;
$: doubled = count * 2;
$: {
console.log('count changed:', count);
}
</script>
Svelte 5 Runes 语法:
svelte
<script>
let count = $state(0);
let doubled = $derived(count * 2);
$effect(() => {
console.log('count changed:', count);
});
</script>
2.5 Script 标签属性 #
svelte
<script lang="ts">
interface Props {
name: string;
count?: number;
}
let { name, count = 0 }: Props = $props();
</script>
<script context="module">
export const staticData = 'shared across instances';
</script>
三、Template 部分 #
3.1 基本模板 #
svelte
<main>
<h1>Hello {name}!</h1>
<p>Count: {count}</p>
</main>
3.2 插值表达式 #
svelte
<script>
let name = 'Svelte';
let count = 42;
let items = ['a', 'b', 'c'];
let user = { name: 'Alice', age: 25 };
</script>
<p>字符串: {name}</p>
<p>数字: {count}</p>
<p>表达式: {count * 2}</p>
<p>方法调用: {name.toUpperCase()}</p>
<p>对象属性: {user.name}</p>
<p>数组长度: {items.length}</p>
<p>三元表达式: {count > 40 ? '大' : '小'}</p>
3.3 条件渲染 #
svelte
{#if count > 10}
<p>大于10</p>
{:else if count > 5}
<p>大于5</p>
{:else}
<p>小于等于5</p>
{/if}
3.4 列表渲染 #
svelte
<script>
let items = [
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' },
{ id: 3, name: 'Item 3' }
];
</script>
<ul>
{#each items as item (item.id)}
<li>{item.name}</li>
{/each}
</ul>
{#each items as item, index (item.id)}
<p>{index + 1}. {item.name}</p>
{/each}
3.5 HTML 插入 #
svelte
<script>
let htmlContent = '<strong>加粗文本</strong>';
</script>
<p>{@html htmlContent}</p>
四、Style 部分 #
4.1 作用域样式 #
svelte
<style>
p {
color: red;
}
.highlight {
background: yellow;
}
</style>
<p class="highlight">这段文字有作用域样式</p>
编译后的 CSS:
css
p.svelte-xyz123 {
color: red;
}
.highlight.svelte-xyz123 {
background: yellow;
}
4.2 全局样式 #
svelte
<style>
:global(body) {
margin: 0;
padding: 0;
}
:global(.global-class) {
font-size: 16px;
}
:global {
* {
box-sizing: border-box;
}
}
</style>
4.3 动态样式 #
svelte
<script>
let color = 'red';
let size = 16;
</script>
<p style="color: {color}; font-size: {size}px;">
动态样式文本
</p>
<div style:color={color} style:font-size="{size}px">
使用 style: 指令
</div>
4.4 动态类名 #
svelte
<script>
let isActive = true;
let isDisabled = false;
let theme = 'dark';
</script>
<button class:active={isActive} class:disabled={isDisabled}>
条件类名
</button>
<div class="box {theme}" class:highlight={isActive}>
混合类名
</div>
五、组件组织 #
5.1 组件命名 #
text
推荐命名规范
├── 文件名: PascalCase(如 Button.svelte)
├── 组件导入: PascalCase
└── 使用: <Button /> 或 <button></button>
5.2 目录结构 #
text
src/
├── lib/
│ ├── components/
│ │ ├── Button.svelte
│ │ ├── Input.svelte
│ │ └── Modal.svelte
│ ├── layouts/
│ │ └── MainLayout.svelte
│ └── utils/
│ └── helpers.js
├── routes/
│ ├── +page.svelte
│ └── +layout.svelte
└── app.css
5.3 组件导入 #
svelte
<script>
import Button from '$lib/components/Button.svelte';
import Input from '$lib/components/Input.svelte';
import { formatDate } from '$lib/utils/helpers.js';
</script>
六、完整组件示例 #
6.1 Card 组件 #
svelte
<script>
let {
title = 'Card Title',
description = '',
imageUrl = null,
onAction = null
} = $props();
</script>
<article class="card">
{#if imageUrl}
<img src={imageUrl} alt={title} class="card-image" />
{/if}
<div class="card-content">
<h2 class="card-title">{title}</h2>
{#if description}
<p class="card-description">{description}</p>
{/if}
{#if onAction}
<button class="card-action" onclick={onAction}>
<slot name="action">查看详情</slot>
</button>
{/if}
</div>
</article>
<style>
.card {
border: 1px solid #e0e0e0;
border-radius: 8px;
overflow: hidden;
background: white;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
transition: transform 0.2s, box-shadow 0.2s;
}
.card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}
.card-image {
width: 100%;
height: 200px;
object-fit: cover;
}
.card-content {
padding: 1rem;
}
.card-title {
margin: 0 0 0.5rem;
font-size: 1.25rem;
color: #333;
}
.card-description {
margin: 0 0 1rem;
color: #666;
line-height: 1.5;
}
.card-action {
padding: 0.5rem 1rem;
background: #ff3e00;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background 0.2s;
}
.card-action:hover {
background: #e63600;
}
</style>
6.2 使用 Card 组件 #
svelte
<script>
import Card from '$lib/components/Card.svelte';
let cards = [
{
title: 'Svelte 入门',
description: '学习 Svelte 的基础知识',
imageUrl: 'https://example.com/svelte.jpg'
},
{
title: 'SvelteKit 实战',
description: '构建全栈应用'
}
];
function handleAction(card) {
alert(`点击了: ${card.title}`);
}
</script>
<div class="card-grid">
{#each cards as card}
<Card
title={card.title}
description={card.description}
imageUrl={card.imageUrl}
onAction={() => handleAction(card)}
>
<span slot="action">了解更多</span>
</Card>
{/each}
</div>
<style>
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 1.5rem;
padding: 1rem;
}
</style>
七、特殊标签 #
7.1 svelte:head #
svelte
<svelte:head>
<title>页面标题</title>
<meta name="description" content="页面描述" />
</svelte:head>
7.2 svelte:body #
svelte
<svelte:body on:mousemove={handleMouseMove} />
7.3 svelte:window #
svelte
<script>
let scrollY = 0;
function handleScroll(e) {
scrollY = e.target.scrollY;
}
</script>
<svelte:window on:scroll={handleScroll} bind:scrollY />
7.4 svelte:fragment #
svelte
<svelte:fragment slot="content">
<p>内容段落1</p>
<p>内容段落2</p>
</svelte:fragment>
八、组件对比 #
8.1 Svelte 4 vs Svelte 5 #
| 特性 | Svelte 4 | Svelte 5 |
|---|---|---|
| Props | export let |
$props() |
| 状态 | let |
$state() |
| 派生 | $: |
$derived() |
| 副作用 | $: |
$effect() |
| 事件 | on:click |
onclick |
| 双向绑定 | bind:value |
bind:value |
8.2 迁移示例 #
Svelte 4:
svelte
<script>
export let count = 0;
$: doubled = count * 2;
</script>
<button on:click={() => count++}>
{count} ({doubled})
</button>
Svelte 5:
svelte
<script>
let { count = 0 } = $props();
let doubled = $derived(count * 2);
</script>
<button onclick={() => count++}>
{count} ({doubled})
</button>
九、总结 #
| 部分 | 说明 |
|---|---|
<script> |
JavaScript 逻辑、状态、方法 |
| Template | HTML 模板、数据绑定 |
<style> |
CSS 样式,自动作用域 |
| 特殊标签 | svelte:head、svelte:window 等 |
组件设计原则:
- 单一职责:每个组件只做一件事
- Props 向下:数据从父组件流向子组件
- 事件向上:子组件通过事件通知父组件
- 样式隔离:使用作用域样式避免冲突
- 可复用:设计通用组件,提高复用性
最后更新:2026-03-28