Перейти к содержимому

10. Svelte в Astro

Svelte — компилятор-фреймворк с минимальным runtime. В отличие от React и Vue, Svelte компилирует компоненты в чистый ванильный JavaScript во время сборки, что даёт значительно меньший размер бандла — идеально для Astro Islands.

Окно терминала
npx astro add svelte

Команда автоматически:

  • Устанавливает @astrojs/svelte и svelte
  • Обновляет astro.config.mjs
  • Настраивает компилятор Svelte
import { defineConfig } from 'astro/config';
import svelte from '@astrojs/svelte';
export default defineConfig({
integrations: [svelte()],
});
src/components/SvelteCounter.svelte
<script>
export let initialCount = 0;
let count = initialCount;
</script>
<div class="counter">
<button on:click={() => count--}>−</button>
<span>{count}</span>
<button on:click={() => count++}>+</button>
</div>
<style>
.counter {
display: flex;
align-items: center;
gap: 12px;
}
span {
font-size: 1.5rem;
font-weight: bold;
}
</style>
---
import SvelteCounter from '../components/SvelteCounter.svelte';
---
<SvelteCounter initialCount={10} client:load />

Svelte stores работают как глобальное состояние и могут использоваться для коммуникации между Islands:

src/store/cart.js
import { writable, derived } from 'svelte/store';
export const cartItems = writable([]);
export const cartTotal = derived(cartItems, ($items) =>
$items.reduce((sum, item) => sum + item.price * item.qty, 0)
);
export const addToCart = (product) => {
cartItems.update(items => {
const existing = items.find(i => i.id === product.id);
if (existing) {
return items.map(i => i.id === product.id ? { ...i, qty: i.qty + 1 } : i);
}
return [...items, { ...product, qty: 1 }];
});
};
src/components/CartIcon.svelte
<script>
import { cartItems } from '../store/cart.js';
</script>
<button>
🛒 {$cartItems.length}
</button>

Svelte предлагает встроенные анимации без дополнительных зависимостей:

src/components/AnimatedList.svelte
<script>
import { fly, fade, scale } from 'svelte/transition';
import { flip } from 'svelte/animate';
let items = ['Astro', 'Svelte', 'Islands'];
let newItem = '';
const add = () => {
if (newItem.trim()) {
items = [...items, newItem.trim()];
newItem = '';
}
};
const remove = (i) => {
items = items.filter((_, index) => index !== i);
};
</script>
<div>
<div class="input-row">
<input bind:value={newItem} on:keydown={(e) => e.key === 'Enter' && add()} />
<button on:click={add}>Добавить</button>
</div>
<ul>
{#each items as item, i (item)}
<li animate:flip={{ duration: 300 }} transition:fly={{ y: -20, duration: 300 }}>
{item}
<button on:click={() => remove(i)}>×</button>
</li>
{/each}
</ul>
</div>

Svelte компилирует компоненты в ванильный JS без необходимости загружать runtime библиотеку:

ФреймворкRuntimeТипичный компонент
React~45kb+1-5kb
Vue~34kb+2-8kb
Svelte~1.5kb+1-3kb
<!-- Svelte компилирует это... -->
<script>
let count = 0;
</script>
<button on:click={() => count++}>{count}</button>
<!-- ...в чистый JS (~300 байт): -->
<!-- function create_fragment(ctx) { ... } -->
src/components/TypedCounter.svelte
<script lang="ts">
export let initialCount: number = 0;
export let step: number = 1;
export let label: string = 'Счётчик';
let count: number = initialCount;
const increment = (): void => { count += step; };
const decrement = (): void => { count -= step; };
const reset = (): void => { count = initialCount; };
</script>
<div>
<h3>{label}: {count}</h3>
<button on:click={decrement}>−{step}</button>
<button on:click={reset}>Сброс</button>
<button on:click={increment}>+{step}</button>
</div>