条件渲染 #

一、基本语法 #

1.1 if 块 #

svelte
<script>
  let show = true;
</script>

{#if show}
  <p>This is visible</p>
{/if}

1.2 if-else 块 #

svelte
<script>
  let count = 5;
</script>

{#if count > 10}
  <p>Count is greater than 10</p>
{:else}
  <p>Count is 10 or less</p>
{/if}

1.3 if-else if-else 块 #

svelte
<script>
  let score = 75;
</script>

{#if score >= 90}
  <p>Grade: A</p>
{:else if score >= 80}
  <p>Grade: B</p>
{:else if score >= 70}
  <p>Grade: C</p>
{:else if score >= 60}
  <p>Grade: D</p>
{:else}
  <p>Grade: F</p>
{/if}

二、条件渲染场景 #

2.1 显示/隐藏 #

svelte
<script>
  let isVisible = false;
  
  function toggle() {
    isVisible = !isVisible;
  }
</script>

<button onclick={toggle}>
  {isVisible ? 'Hide' : 'Show'}
</button>

{#if isVisible}
  <div class="content">
    This content can be toggled
  </div>
{/if}

2.2 加载状态 #

svelte
<script>
  let loading = $state(true);
  let data = $state(null);
  let error = $state(null);
  
  async function fetchData() {
    loading = true;
    error = null;
    
    try {
      const response = await fetch('/api/data');
      data = await response.json();
    } catch (e) {
      error = e.message;
    } finally {
      loading = false;
    }
  }
  
  fetchData();
</script>

{#if loading}
  <div class="loading">
    <span class="spinner"></span>
    Loading...
  </div>
{:else if error}
  <div class="error">
    Error: {error}
  </div>
{:else if data}
  <div class="data">
    {#each data as item}
      <p>{item.name}</p>
    {/each}
  </div>
{:else}
  <div class="empty">
    No data available
  </div>
{/if}

2.3 用户状态 #

svelte
<script>
  let user = $state(null);
  let isLoggedIn = $derived(!!user);
  
  function login() {
    user = { name: 'Alice', role: 'admin' };
  }
  
  function logout() {
    user = null;
  }
</script>

{#if isLoggedIn}
  <div class="user-panel">
    <p>Welcome, {user.name}!</p>
    
    {#if user.role === 'admin'}
      <button>Admin Panel</button>
    {/if}
    
    <button onclick={logout}>Logout</button>
  </div>
{:else}
  <div class="login-panel">
    <button onclick={login}>Login</button>
  </div>
{/if}

2.4 表单验证 #

svelte
<script>
  let form = $state({
    email: '',
    password: ''
  });
  
  let touched = $state({});
  let errors = $derived({
    email: touched.email && !form.email ? 'Email required' : null,
    password: touched.password && form.password.length < 6 
      ? 'Password must be at least 6 characters' 
      : null
  });
  
  let isValid = $derived(!errors.email && !errors.password);
</script>

<form>
  <div>
    <input 
      type="email" 
      bind:value={form.email}
      onblur={() => touched = { ...touched, email: true }}
    />
    {#if errors.email}
      <span class="error">{errors.email}</span>
    {/if}
  </div>
  
  <div>
    <input 
      type="password" 
      bind:value={form.password}
      onblur={() => touched = { ...touched, password: true }}
    />
    {#if errors.password}
      <span class="error">{errors.password}</span>
    {/if}
  </div>
  
  {#if isValid}
    <button type="submit">Submit</button>
  {:else}
    <button type="submit" disabled>Submit</button>
  {/if}
</form>

三、嵌套条件 #

3.1 多层嵌套 #

svelte
<script>
  let user = $state(null);
  let notifications = $state([]);
</script>

{#if user}
  {#if notifications.length > 0}
    {#if notifications.length > 10}
      <p>You have many notifications ({notifications.length})</p>
    {:else}
      <p>You have {notifications.length} notifications</p>
    {/if}
  {:else}
    <p>No notifications</p>
  {/if}
{:else}
  <p>Please login to see notifications</p>
{/if}

3.2 简化嵌套 #

svelte
<script>
  let user = $state(null);
  let notifications = $state([]);
  
  let showManyNotifications = $derived(
    user && notifications.length > 10
  );
  
  let showNormalNotifications = $derived(
    user && notifications.length > 0 && notifications.length <= 10
  );
  
  let showNoNotifications = $derived(
    user && notifications.length === 0
  );
</script>

{#if showManyNotifications}
  <p>You have many notifications ({notifications.length})</p>
{:else if showNormalNotifications}
  <p>You have {notifications.length} notifications</p>
{:else if showNoNotifications}
  <p>No notifications</p>
{:else}
  <p>Please login to see notifications</p>
{/if}

四、条件与过渡 #

4.1 条件过渡 #

svelte
<script>
  import { fade, fly, slide } from 'svelte/transition';
  
  let show = false;
</script>

<button onclick={() => show = !show}>
  Toggle
</button>

{#if show}
  <div transition:fade>
    Fading in and out
  </div>
{/if}

{#if show}
  <div in:fly={{ y: 50 }} out:fade>
    Flying in, fading out
  </div>
{/if}

4.2 带延迟的过渡 #

svelte
<script>
  import { fade } from 'svelte/transition';
  
  let items = $state([]);
  
  function addItem() {
    items = [...items, { id: Date.now() }];
  }
  
  function removeItem(id) {
    items = items.filter(item => item.id !== id);
  }
</script>

<button onclick={addItem}>Add Item</button>

{#each items as item (item.id)}
  {#if true}
    <div transition:fade={{ delay: items.indexOf(item) * 100 }}>
      Item {item.id}
      <button onclick={() => removeItem(item.id)}>Remove</button>
    </div>
  {/if}
{/each}

五、条件渲染 vs CSS 隐藏 #

5.1 条件渲染 #

svelte
{#if show}
  <div>This is conditionally rendered</div>
{/if}

特点:

  • 元素完全不存在于 DOM 中
  • 不占用资源
  • 组件状态不保留

5.2 CSS 隐藏 #

svelte
<script>
  let show = true;
</script>

<div class:hidden={!show}>
  This is hidden with CSS
</div>

<style>
  .hidden {
    display: none;
  }
</style>

特点:

  • 元素存在于 DOM 中
  • 占用资源
  • 组件状态保留

5.3 选择建议 #

text
使用条件渲染:
├── 组件初始化开销大
├── 需要完全移除元素
├── SEO 考虑
└── 性能敏感场景

使用 CSS 隐藏:
├── 需要保留组件状态
├── 频繁切换显示
├── 过渡动画需要
└── 表单输入保留

六、实际应用示例 #

6.1 消息提示组件 #

svelte
<script>
  let { type = 'info', message = '', duration = 3000 } = $props();
  
  let visible = $state(false);
  
  export function show(msg, tp = type) {
    message = msg;
    type = tp;
    visible = true;
    
    setTimeout(() => {
      visible = false;
    }, duration);
  }
</script>

{#if visible}
  <div class="message {type}" transition:fade>
    {message}
    <button onclick={() => visible = false}>×</button>
  </div>
{/if}

<style>
  .message {
    padding: 1rem;
    border-radius: 4px;
    display: flex;
    justify-content: space-between;
    align-items: center;
  }
  
  .info { background: #e3f2fd; color: #1976d2; }
  .success { background: #e8f5e9; color: #388e3c; }
  .warning { background: #fff3e0; color: #f57c00; }
  .error { background: #ffebee; color: #d32f2f; }
</style>

6.2 标签页组件 #

svelte
<script>
  let { tabs = [], defaultTab = 0 } = $props();
  
  let activeTab = $state(defaultTab);
</script>

<div class="tabs">
  <div class="tab-list">
    {#each tabs as tab, index}
      <button 
        class="tab-button"
        class:active={activeTab === index}
        onclick={() => activeTab = index}
      >
        {tab.label}
      </button>
    {/each}
  </div>
  
  <div class="tab-content">
    {#each tabs as tab, index}
      {#if activeTab === index}
        <div class="tab-panel" transition:fade>
          {tab.content}
        </div>
      {/if}
    {/each}
  </div>
</div>

<style>
  .tabs {
    border: 1px solid #ddd;
    border-radius: 4px;
  }
  
  .tab-list {
    display: flex;
    border-bottom: 1px solid #ddd;
  }
  
  .tab-button {
    padding: 0.75rem 1.5rem;
    border: none;
    background: none;
    cursor: pointer;
    border-bottom: 2px solid transparent;
  }
  
  .tab-button.active {
    border-bottom-color: #ff3e00;
    color: #ff3e00;
  }
  
  .tab-content {
    padding: 1rem;
  }
</style>

6.3 步骤向导 #

svelte
<script>
  let steps = [
    { title: 'Step 1', description: 'Personal Info' },
    { title: 'Step 2', description: 'Account Setup' },
    { title: 'Step 3', description: 'Confirmation' }
  ];
  
  let currentStep = $state(0);
  
  let isFirstStep = $derived(currentStep === 0);
  let isLastStep = $derived(currentStep === steps.length - 1);
  
  function next() {
    if (!isLastStep) currentStep += 1;
  }
  
  function prev() {
    if (!isFirstStep) currentStep -= 1;
  }
</script>

<div class="wizard">
  <div class="steps">
    {#each steps as step, index}
      <div 
        class="step"
        class:active={index === currentStep}
        class:completed={index < currentStep}
      >
        <span class="step-number">
          {#if index < currentStep}✓{:else}{index + 1}{/if}
        </span>
        <span class="step-title">{step.title}</span>
      </div>
    {/each}
  </div>
  
  <div class="content">
    {#if currentStep === 0}
      <h2>Personal Info</h2>
      <input type="text" placeholder="Name" />
    {:else if currentStep === 1}
      <h2>Account Setup</h2>
      <input type="email" placeholder="Email" />
      <input type="password" placeholder="Password" />
    {:else if currentStep === 2}
      <h2>Confirmation</h2>
      <p>Review your information</p>
    {/if}
  </div>
  
  <div class="actions">
    {#if !isFirstStep}
      <button onclick={prev}>Previous</button>
    {/if}
    
    {#if !isLastStep}
      <button onclick={next}>Next</button>
    {:else}
      <button>Finish</button>
    {/if}
  </div>
</div>

七、性能考虑 #

7.1 避免复杂条件 #

svelte
<script>
  let data = $state([]);
  
  let hasData = $derived(data.length > 0);
  let hasManyItems = $derived(data.length > 100);
  let hasErrors = $derived(data.some(item => item.error));
</script>

{#if hasErrors}
  <div class="error">Some items have errors</div>
{:else if hasManyItems}
  <div class="warning">Many items</div>
{:else if hasData}
  <div class="success">Data loaded</div>
{:else}
  <div class="empty">No data</div>
{/if}

7.2 使用 key 优化 #

svelte
<script>
  let items = $state([]);
  let selectedId = $state(null);
</script>

{#each items as item (item.id)}
  {#if item.id === selectedId}
    <div class="selected">
      {item.name} (selected)
    </div>
  {:else}
    <div class="item">
      {item.name}
    </div>
  {/if}
{/each}

八、总结 #

语法 说明
{#if condition} 条件开始
{:else if condition} 否则如果
{:else} 否则
{/if} 条件结束

条件渲染要点:

  • 使用 {#if} 开始条件块
  • 使用 {:else}{:else if} 添加分支
  • 必须以 {/if} 结束
  • 可以嵌套使用
  • 结合过渡动画提升用户体验
  • 合理选择条件渲染或 CSS 隐藏
最后更新:2026-03-28