性能优化 #

一、性能优化概述 #

Svelte 本身已经非常高效,但仍有一些优化技巧可以进一步提升性能。

text
┌─────────────────────────────────────────────────────────────┐
│                    Svelte 性能优化                           │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  编译时优化                                                  │
│  ├── 静态内容提升                                           │
│  ├── 事件委托                                               │
│  ├── CSS 作用域                                             │
│  └── Tree Shaking                                           │
│                                                             │
│  运行时优化                                                  │
│  ├── 减少响应式依赖                                         │
│  ├── 合理使用 key                                           │
│  ├── 懒加载组件                                             │
│  └── 虚拟滚动                                               │
│                                                             │
│  网络优化                                                    │
│  ├── 代码分割                                               │
│  ├── 预取资源                                               │
│  └── 缓存策略                                               │
│                                                             │
└─────────────────────────────────────────────────────────────┘

二、编译时优化 #

2.1 静态内容识别 #

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

<!-- 静态内容会被提升 -->
<header>
  <h1>My Website</h1>
  <nav>
    <a href="/">Home</a>
    <a href="/about">About</a>
  </nav>
</header>

<!-- 动态内容 -->
<main>
  <p>Count: {count}</p>
  <button onclick={() => count++}>Increment</button>
</main>

2.2 减少响应式依赖 #

svelte
<script>
  let items = $state([]);
  let filter = $state('');
  
  // 不推荐:每次 filter 变化都重新计算
  let filtered = $derived(items.filter(i => i.includes(filter)));
  
  // 推荐:分离计算
  function getFiltered() {
    return items.filter(i => i.includes(filter));
  }
</script>

<!-- 使用函数 -->
{#each getFiltered() as item}
  {item}
{/each}

2.3 使用不可变数据 #

svelte
<script>
  // 推荐:使用 immutable 选项
</script>

<svelte:options immutable={true} />

<script>
  let items = $state([]);
  
  // 不可变更新
  function addItem(item) {
    items = [...items, item];
  }
</script>

三、列表渲染优化 #

3.1 使用 Key #

svelte
<script>
  let items = $state([
    { id: 1, name: 'Item 1' },
    { id: 2, name: 'Item 2' },
    { id: 3, name: 'Item 3' }
  ]);
</script>

<!-- 推荐:使用唯一 key -->
{#each items as item (item.id)}
  <div>{item.name}</div>
{/each}

<!-- 不推荐:无 key -->
{#each items as item}
  <div>{item.name}</div>
{/each}

3.2 虚拟滚动 #

svelte
<script>
  let allItems = Array.from({ length: 10000 }, (_, i) => ({
    id: i,
    name: `Item ${i}`
  }));
  
  let scrollTop = $state(0);
  let containerHeight = 400;
  let itemHeight = 40;
  let visibleCount = Math.ceil(containerHeight / itemHeight) + 2;
  
  let startIndex = $derived(Math.floor(scrollTop / itemHeight));
  let endIndex = $derived(Math.min(startIndex + visibleCount, allItems.length));
  
  let visibleItems = $derived(allItems.slice(startIndex, endIndex));
  let totalHeight = allItems.length * itemHeight;
  let offsetY = $derived(startIndex * itemHeight);
</script>

<div 
  class="virtual-list" 
  style="height: {containerHeight}px"
  onscroll={(e) => scrollTop = e.target.scrollTop}
>
  <div style="height: {totalHeight}px; position: relative;">
    <div style="position: absolute; top: {offsetY}px; width: 100%;">
      {#each visibleItems as item (item.id)}
        <div style="height: {itemHeight}px">
          {item.name}
        </div>
      {/each}
    </div>
  </div>
</div>

<style>
  .virtual-list {
    overflow-y: auto;
    border: 1px solid #ddd;
  }
</style>

3.3 分页加载 #

svelte
<script>
  let allItems = $state([]);
  let page = $state(1);
  let pageSize = 20;
  let loading = $state(false);
  let hasMore = $state(true);
  
  let displayedItems = $derived(allItems.slice(0, page * pageSize));
  
  async function loadMore() {
    if (loading || !hasMore) return;
    
    loading = true;
    const newItems = await fetchItems(page, pageSize);
    
    if (newItems.length < pageSize) {
      hasMore = false;
    }
    
    allItems = [...allItems, ...newItems];
    page += 1;
    loading = false;
  }
  
  function handleScroll(event) {
    const { scrollTop, scrollHeight, clientHeight } = event.target;
    if (scrollHeight - scrollTop <= clientHeight + 100) {
      loadMore();
    }
  }
</script>

<div class="list" onscroll={handleScroll}>
  {#each displayedItems as item (item.id)}
    <div class="item">{item.name}</div>
  {/each}
  
  {#if loading}
    <div class="loading">Loading...</div>
  {/if}
</div>

四、组件优化 #

4.1 懒加载组件 #

svelte
<script>
  import { onMount } from 'svelte';
  
  let HeavyComponent = null;
  let showHeavy = $state(false);
  
  onMount(async () => {
    if (showHeavy) {
      const module = await import('./HeavyComponent.svelte');
      HeavyComponent = module.default;
    }
  });
  
  $effect(() => {
    if (showHeavy && !HeavyComponent) {
      import('./HeavyComponent.svelte').then(module => {
        HeavyComponent = module.default;
      });
    }
  });
</script>

<button onclick={() => showHeavy = true}>Show Heavy Component</button>

{#if HeavyComponent}
  <svelte:component this={HeavyComponent} />
{/if}

4.2 组件拆分 #

svelte
<!-- 不推荐:大组件 -->
<script>
  // 大量逻辑
</script>

<div>
  <!-- 大量模板 -->
</div>

<!-- 推荐:拆分小组件 -->
<script>
  import Header from './Header.svelte';
  import Sidebar from './Sidebar.svelte';
  import Content from './Content.svelte';
  import Footer from './Footer.svelte';
</script>

<Header />
<div class="main">
  <Sidebar />
  <Content />
</div>
<Footer />

4.3 条件渲染优化 #

svelte
<script>
  let showDetails = $state(false);
  let data = $state(null);
</script>

<!-- 推荐:条件渲染,不渲染时不占用资源 -->
{#if showDetails}
  <DetailsComponent {data} />
{/if}

<!-- 不推荐:CSS 隐藏,组件仍然渲染 -->
<div class:hidden={!showDetails}>
  <DetailsComponent {data} />
</div>

五、Store 优化 #

5.1 避免过度订阅 #

svelte
<script>
  import { user } from '$lib/stores';
  
  // 推荐:只订阅需要的属性
  let userName = $derived($user?.name);
  
  // 不推荐:订阅整个对象
  // let userData = $user;
</script>

<p>{userName}</p>

5.2 派生 Store 缓存 #

typescript
import { derived, writable } from 'svelte/store';

const items = writable([]);

// 派生 Store 自动缓存
export const filteredItems = derived(
  items,
  $items => $items.filter(item => item.active)
);

// 复杂计算可以手动缓存
let cachedResult = null;
let cachedDeps = null;

export const expensiveDerived = derived(
  [items, filter],
  ([$items, $filter]) => {
    const deps = JSON.stringify({ items: $items.length, filter: $filter });
    
    if (deps === cachedDeps) {
      return cachedResult;
    }
    
    cachedDeps = deps;
    cachedResult = expensiveComputation($items, $filter);
    
    return cachedResult;
  }
);

5.3 批量更新 #

svelte
<script>
  let items = $state([]);
  
  // 推荐:批量更新
  function addMultiple(newItems) {
    items = [...items, ...newItems];
  }
  
  // 不推荐:多次更新
  function addOneByOne(newItems) {
    newItems.forEach(item => {
      items = [...items, item];
    });
  }
</script>

六、网络优化 #

6.1 预取数据 #

svelte
<script>
  import { prefetch } from '$app/navigation';
</script>

<a 
  href="/blog/post" 
  onmouseenter={() => prefetch('/blog/post')}
>
  Read Post
</a>

6.2 数据缓存 #

typescript
const cache = new Map<string, { data: unknown; timestamp: number }>();
const CACHE_TTL = 5 * 60 * 1000;

export async function fetchWithCache<T>(
  url: string, 
  options?: RequestInit
): Promise<T> {
  const cached = cache.get(url);
  
  if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
    return cached.data as T;
  }
  
  const response = await fetch(url, options);
  const data = await response.json();
  
  cache.set(url, { data, timestamp: Date.now() });
  
  return data;
}

6.3 图片懒加载 #

svelte
<script>
  function lazyload(node: HTMLImageElement, { src }: { src: string }) {
    const observer = new IntersectionObserver(
      (entries) => {
        if (entries[0].isIntersecting) {
          node.src = src;
          observer.unobserve(node);
        }
      },
      { rootMargin: '100px' }
    );
    
    observer.observe(node);
    
    return {
      destroy() {
        observer.unobserve(node);
      }
    };
  }
</script>

<img 
  use:lazyload={{ src: '/images/large.jpg' }}
  alt="Lazy loaded image"
  loading="lazy"
/>

七、内存优化 #

7.1 清理定时器 #

svelte
<script>
  let count = $state(0);
  
  $effect(() => {
    const timer = setInterval(() => {
      count += 1;
    }, 1000);
    
    return () => {
      clearInterval(timer);
    };
  });
</script>

7.2 清理事件监听 #

svelte
<script>
  $effect(() => {
    function handleResize() {
      console.log('Resized');
    }
    
    window.addEventListener('resize', handleResize);
    
    return () => {
      window.removeEventListener('resize', handleResize);
    };
  });
</script>

7.3 清理订阅 #

svelte
<script>
  import { onMount } from 'svelte';
  import { user } from '$lib/stores';
  
  onMount(() => {
    const unsubscribe = user.subscribe(value => {
      console.log('User changed:', value);
    });
    
    return unsubscribe;
  });
</script>

八、性能监控 #

8.1 性能测量 #

svelte
<script>
  import { onMount } from 'svelte';
  
  onMount(() => {
    performance.mark('component-mounted');
    
    return () => {
      performance.mark('component-destroyed');
      performance.measure(
        'component-lifetime',
        'component-mounted',
        'component-destroyed'
      );
      
      const measure = performance.getEntriesByName('component-lifetime')[0];
      console.log(`Component lifetime: ${measure.duration}ms`);
    };
  });
</script>

8.2 渲染性能 #

svelte
<script>
  let renderCount = 0;
  
  $effect(() => {
    renderCount++;
    console.log(`Render #${renderCount}`);
  });
</script>

8.3 使用 Svelte DevTools #

text
Svelte DevTools 功能
├── 组件树查看
├── 状态检查
├── 性能分析
└── Store 监控

九、最佳实践清单 #

9.1 编译优化 #

text
✅ 检查清单
├── 使用最新版本的 Svelte
├── 启用生产模式构建
├── 使用 Vite 的代码分割
├── 配置 Tree Shaking
└── 压缩 CSS 和 JavaScript

9.2 运行时优化 #

text
✅ 检查清单
├── 列表使用 key
├── 大列表使用虚拟滚动
├── 懒加载重型组件
├── 避免不必要的响应式依赖
├── 及时清理定时器和订阅
└── 使用 $derived 缓存计算

9.3 网络优化 #

text
✅ 检查清单
├── 预取关键资源
├── 图片懒加载
├── 使用 CDN
├── 启用 Gzip/Brotli 压缩
├── 缓存 API 响应
└── 代码分割

十、总结 #

优化类型 技术
编译时 静态提升、Tree Shaking
列表渲染 Key、虚拟滚动、分页
组件 懒加载、拆分、条件渲染
Store 派生缓存、批量更新
网络 预取、缓存、懒加载
内存 清理定时器、订阅

性能优化要点:

  • 使用 key 优化列表渲染
  • 大列表使用虚拟滚动
  • 懒加载重型组件
  • 及时清理资源
  • 合理使用派生缓存
  • 监控性能指标
最后更新:2026-03-28