Manual Completo de Svelte Desde Básico hasta Avanzado

Guía exhaustiva para dominar Svelte, el framework revolucionario que compila a JavaScript vanilla.

Fundamentos
Intermedio
Avanzado
SvelteKit

1. Fundamentos de Svelte

1.1 ¿Qué es Svelte?

Svelte es un compilador que convierte componentes en JavaScript eficiente durante el build, eliminando la necesidad de un Virtual DOM.

1.2 Configuración del Entorno

Terminal
# Crear proyecto con Vite (recomendado)
npm create vite@latest mi-app-svelte --template svelte
cd mi-app-svelte
npm install

# Alternativa con degit (plantilla oficial)
npx degit sveltejs/template mi-app-svelte
cd mi-app-svelte
npm install

1.3 Estructura de un Componente

src/App.svelte
<script>
    let count = 0;

    function increment() {
        count += 1;
    }
</script>

<main>
    <h1>Hola Mundo Svelte!</h1>
    <p>Contador: {count}</p>
    <button on:click={increment}>
        Incrementar
    </button>
</main>

<style>
    h1 {
        color: #ff3e00;
    }
</style>

1.4 Reactividad

En Svelte, la reactividad es automática cuando se asignan valores:

<script>
    let name = 'Mundo';
    let counter = 0;
    
    // Función reactiva
    $: doubled = counter * 2;
    
    // Declaración reactiva
    $: {
        console.log(`El contador es: ${counter}`);
    }
</script>

<input bind:value={name}>
<p>Hola {name}!</p>
<button on:click={() => counter++}>
    Clics: {counter} (Doble: {doubled})
</button>

1.5 Props

src/components/Greeting.svelte
<script>
    export let name = 'Mundo'; // Prop con valor por defecto
</script>

<p>Hola, {name}!</p>
src/App.svelte
<script>
    import Greeting from './components/Greeting.svelte';
    let userName = 'Juan';
</script>

<Greeting name={userName} />

1.6 Bindings

Svelte permite enlace bidireccional con bind::

<script>
    let name = '';
    let isChecked = false;
    let selectedOption = 'A';
</script>

<input bind:value={name} placeholder="Tu nombre">
<p>Hola, {name || 'anónimo'}</p>

<input type="checkbox" bind:checked={isChecked}>
<p>Checkbox {isChecked ? 'marcado' : 'desmarcado'}</p>

<select bind:value={selectedOption}>
    <option value="A">Opción A</option>
    <option value="B">Opción B</option>
</select>
<p>Seleccionado: {selectedOption}</p>

1.7 Eventos

<script>
    function handleClick(event) {
        alert('Botón clickeado!');
    }
    
    let mousePosition = { x: 0, y: 0 };
    
    function handleMousemove(event) {
        mousePosition = { x: event.clientX, y: event.clientY };
    }
</script>

<button on:click={handleClick}>
    Haz clic
</button>

<div on:mousemove={handleMousemove}>
    Mueve el mouse aquí: {mousePosition.x}, {mousePosition.y}
</div>

2. Svelte Intermedio

2.1 Stores (Gestión de Estado)

Svelte ofrece stores para manejar estado global:

writable store

src/stores/counter.js
import { writable } from 'svelte/store';

export const count = writable(0);

// Métodos:
// count.set(n) - Establecer valor
// count.update(n => n + 1) - Actualizar basado en valor anterior
// count.subscribe(callback) - Suscribirse a cambios
src/components/Counter.svelte
<script>
    import { count } from '../stores/counter';
    
    function increment() {
        count.update(n => n + 1);
    }
</script>

<button on:click={increment}>
    Clics: {$count}
</button>

readable store

src/stores/time.js
import { readable } from 'svelte/store';

export const time = readable(new Date(), set => {
    const interval = setInterval(() => {
        set(new Date());
    }, 1000);
    
    return () => clearInterval(interval);
});

derived store

import { derived } from 'svelte/store';
import { time } from './time';

export const elapsed = derived(
    time,
    $time => Math.floor($time / 1000)
);

2.2 Transiciones y Animaciones

src/components/Fade.svelte
<script>
    import { fade, fly } from 'svelte/transition';
    import { elasticOut } from 'svelte/easing';
    let visible = true;
</script>

<button on:click={() => visible = !visible}>
    Toggle
</button>

{#if visible}
    <div transition:fade={{ duration: 500 }}>
        Aparece/Desaparece
    </div>
    
    <div transition:fly={{
        y: 50,
        duration: 800,
        easing: elasticOut
    }}>
        Vuela hacia arriba/abajo
    </div>
{/if}

2.3 Slots

src/components/Card.svelte
<div class="card">
    <slot name="header">Título por defecto</slot>
    <slot>Contenido por defecto</slot>
    <slot name="footer"></slot>
</div>
src/App.svelte
<Card>
    <h1 slot="header">Título personalizado</h1>
    <p>Contenido personalizado</p>
    <button slot="footer">Acción</button>
</Card>

2.4 Context API

src/components/Parent.svelte
<script>
    import { setContext } from 'svelte';
    import Child from './Child.svelte';
    
    setContext('theme', 'dark');
</script>

<Child />
src/components/Child.svelte
<script>
    import { getContext } from 'svelte';
    
    const theme = getContext('theme');
</script>

<p>Theme: {theme}</p>

2.5 Actions

src/actions/longpress.js
export function longpress(node, duration) {
    let timer;
    
    const handleMousedown = () => {
        timer = setTimeout(() => {
            node.dispatchEvent(new CustomEvent('longpress'));
        }, duration);
    };
    
    const handleMouseup = () => {
        clearTimeout(timer);
    };
    
    node.addEventListener('mousedown', handleMousedown);
    node.addEventListener('mouseup', handleMouseup);
    
    return {
        destroy() {
            node.removeEventListener('mousedown', handleMousedown);
            node.removeEventListener('mouseup', handleMouseup);
        }
    };
}
src/App.svelte
<script>
    import { longpress } from './actions/longpress.js';
    
    function handleLongpress() {
        alert('Long press detected!');
    }
</script>

<button 
    use:longpress={500}
    on:longpress={handleLongpress}
>
    Mantén presionado
</button>

3. Svelte Avanzado

3.1 Custom Stores

src/stores/auth.js
import { writable } from 'svelte/store';

function createAuthStore() {
    const { subscribe, set } = writable(null);
    
    return {
        subscribe,
        login: (user) => set(user),
        logout: () => set(null)
    };
}

export const auth = createAuthStore();
src/components/Auth.svelte
<script>
    import { auth } from '../stores/auth';
</script>

{#if $auth}
    <p>Bienvenido, {$auth.name}</p>
    <button on:click={() => auth.logout()}>
        Cerrar sesión
    </button>
{:else}
    <button on:click={() => auth.login({ name: 'Usuario' })}>
        Iniciar sesión
    </button>
{/if}

3.2 Class Binding Dinámico

<script>
    let isActive = true;
    let error = false;
</script>

<div class:active={isActive} class:error>
    Clases dinámicas
</div>

<!-- Equivalente a: -->
<div class={`${isActive ? 'active' : ''} ${error ? 'error' : ''}`}>
    Clases dinámicas
</div>

3.3 Componentes Dinámicos

src/components/Dynamic.svelte
<script>
    import ComponentA from './ComponentA.svelte';
    import ComponentB from './ComponentB.svelte';
    
    let components = {
        a: ComponentA,
        b: ComponentB
    };
    
    let current = 'a';
</script>

<select bind:value={current}>
    <option value="a">Componente A</option>
    <option value="b">Componente B</option>
</select>

<svelte:component this={components[current]} />

3.4 Portal de Componentes

src/components/Modal.svelte
<script>
    import { onMount } from 'svelte';
    
    let modal;
    onMount(() => {
        modal = document.createElement('div');
        document.body.appendChild(modal);
        
        return () => {
            document.body.removeChild(modal);
        };
    });
</script>

<svelte:component this={ModalContent} {close} bind:this={modal} />

3.5 SSR y Hydratación

Svelte soporta Server-Side Rendering (SSR) con SvelteKit:

src/routes/+page.svelte
<script context="module">
    export async function load({ fetch }) {
        const res = await fetch('/api/data');
        const data = await res.json();
        
        return {
            props: { data }
        };
    }
</script>

<script>
    export let data;
</script>

<p>Datos cargados en el servidor: {data.message}</p>

4. SvelteKit: Aplicaciones Complejas

4.1 Introducción a SvelteKit

SvelteKit es el framework oficial para construir aplicaciones Svelte:

4.2 Crear un Proyecto SvelteKit

Terminal
npm create svelte@latest mi-app-sveltekit
cd mi-app-sveltekit
npm install
npm run dev

4.3 Estructura del Proyecto

src/
├── app.html          # Plantilla HTML base
├── routes/
│   ├── +page.svelte # Página principal
│   ├── about/
│   │   └── +page.svelte # /about
│   └── blog/
│       ├── +page.svelte # /blog
│       └── [slug]/
│           └── +page.svelte # /blog/:slug
├── lib/              # Código reutilizable
└── app.d.ts          # Tipos globales

4.4 Rutas y Layouts

Layout Principal

src/routes/+layout.svelte
<nav>
    <a href="/">Home</a>
    <a href="/about">About</a>
</nav>

<slot /> <!-- Contenido de las páginas -->

Página Dinámica

src/routes/blog/[slug]/+page.svelte
<script>
    import { page } from '$app/stores';
</script>

<h1>Post: {$page.params.slug}</h1>

4.5 Data Loading

src/routes/blog/+page.svelte
<script context="module">
    export async function load({ fetch }) {
        const res = await fetch('https://api.example.com/posts');
        const posts = await res.json();
        
        return {
            props: { posts }
        };
    }
</script>

<script>
    export let posts;
</script>

{#each posts as post}
    <article>
        <h2>{post.title}</h2>
        <p>{post.excerpt}</p>
    </article>
{/each}

4.6 Formularios y Actions

src/routes/contact/+page.svelte
<script>
    import { enhance } from '$app/forms';
    
    let form;
</script>

<form method="POST" use:enhance>
    <input name="email" type="email" bind:value={form.email}>
    <textarea name="message" bind:value={form.message}></textarea>
    <button type="submit">Enviar</button>
</form>
src/routes/contact/+page.server.js
export const actions = {
    default: async ({ request }) => {
        const formData = await request.formData();
        const email = formData.get('email');
        const message = formData.get('message');
        
        // Procesar el formulario...
        
        return {
            success: true
        };
    }
};

4.7 Autenticación

src/hooks.server.js
import { sequence } from '@sveltejs/kit/hooks';

async function auth({ event, resolve }) {
    const session = event.cookies.get('session');
    event.locals.user = session ? getUser(session) : null;
    
    return await resolve(event);
}

export const handle = sequence(auth);
src/routes/+layout.server.js
export function load({ locals }) {
    return {
        user: locals.user
    };
}

4.8 Despliegue

SvelteKit soporta múltiples adaptadores:

Terminal
# Adaptador para Node.js
npm install @sveltejs/adapter-node

# Adaptador para Vercel
npm install @sveltejs/adapter-vercel

# Adaptador para Static Site
npm install @sveltejs/adapter-static
svelte.config.js
import adapter from '@sveltejs/adapter-auto';
import { vitePreprocess } from '@sveltejs/kit/vite';

export default {
    kit: {
        adapter: adapter()
    },
    preprocess: vitePreprocess()
};

Comandos de construcción:

# Desarrollo
npm run dev

# Producción
npm run build
npm run preview

# Static Site Generation
npm run build
npm run preview