性能优化 #
一、性能优化概述 #
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