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