0b76a4b119
- Delete Mode1Test, Mode2Test, Mode3Test, Mode5Test (keep only Mode6Test) - Delete old documentation, Docker files, and k8s configs - Add comprehensive Svelte 5 reference library (svelte-reference/) - Update ModeSwitch orchestrator: - Fix port to 3005 (never use 3000/3001) - Add API endpoints for mode switching - Add bun-types and proper tsconfig - Clean up mode folder mappings
344 lines
9.0 KiB
Markdown
344 lines
9.0 KiB
Markdown
# Svelte 5 Quick Reference Card
|
|
|
|
## Runes Cheat Sheet
|
|
|
|
```svelte
|
|
<script>
|
|
// ═══════════════════════════════════════════════════════════
|
|
// $state - Reactive state declaration
|
|
// ═══════════════════════════════════════════════════════════
|
|
let count = $state(0); // Primitive
|
|
let user = $state({ name: '', age: 0 }); // Object (deep reactive)
|
|
let items = $state<string[]>([]); // Array (deep reactive)
|
|
|
|
// Direct mutations work with $state!
|
|
items.push('item'); // ✅ Reactive
|
|
user.name = 'John'; // ✅ Reactive
|
|
count++; // ✅ Reactive
|
|
|
|
// ═══════════════════════════════════════════════════════════
|
|
// $derived - Computed values (auto-tracks dependencies)
|
|
// ═══════════════════════════════════════════════════════════
|
|
let doubled = $derived(count * 2); // Simple expression
|
|
|
|
let filtered = $derived.by(() => { // Complex computation
|
|
return items.filter(i => i.includes(filter));
|
|
});
|
|
|
|
// ═══════════════════════════════════════════════════════════
|
|
// $effect - Side effects (auto-tracks, auto-cleanup)
|
|
// ═══════════════════════════════════════════════════════════
|
|
$effect(() => {
|
|
console.log('Count is:', count); // Runs when count changes
|
|
|
|
return () => { // Optional cleanup
|
|
console.log('Cleaning up');
|
|
};
|
|
});
|
|
|
|
$effect.pre(() => {
|
|
// Runs BEFORE DOM updates
|
|
});
|
|
|
|
// ═══════════════════════════════════════════════════════════
|
|
// $props - Component props
|
|
// ═══════════════════════════════════════════════════════════
|
|
let {
|
|
name, // Required prop
|
|
count = 0, // With default
|
|
children, // Snippet (replaces default slot)
|
|
header, // Named snippet
|
|
onclick, // Event callback
|
|
...rest // Rest props
|
|
} = $props();
|
|
|
|
// ═══════════════════════════════════════════════════════════
|
|
// $bindable - Two-way bindable props
|
|
// ═══════════════════════════════════════════════════════════
|
|
let { value = $bindable('') } = $props();
|
|
// Parent: <Input bind:value />
|
|
|
|
// ═══════════════════════════════════════════════════════════
|
|
// $inspect - Debug reactive values (dev only)
|
|
// ═══════════════════════════════════════════════════════════
|
|
$inspect(count); // Logs on change
|
|
$inspect(count).with(console.trace); // Custom logger
|
|
</script>
|
|
```
|
|
|
|
---
|
|
|
|
## Template Syntax
|
|
|
|
```svelte
|
|
<!-- Rendering Snippets (replaces slots) -->
|
|
{@render children?.()}
|
|
{@render header?.()}
|
|
{@render row({ item, index })}
|
|
|
|
<!-- Conditionals -->
|
|
{#if condition}
|
|
...
|
|
{:else if other}
|
|
...
|
|
{:else}
|
|
...
|
|
{/if}
|
|
|
|
<!-- Loops -->
|
|
{#each items as item, index (item.id)}
|
|
...
|
|
{/each}
|
|
|
|
<!-- Await -->
|
|
{#await promise}
|
|
<Loading />
|
|
{:then data}
|
|
<Result {data} />
|
|
{:catch error}
|
|
<Error {error} />
|
|
{/await}
|
|
|
|
<!-- Key block (force re-render) -->
|
|
{#key value}
|
|
<Component />
|
|
{/key}
|
|
|
|
<!-- HTML (dangerous) -->
|
|
{@html content}
|
|
|
|
<!-- Debug -->
|
|
{@debug variable}
|
|
|
|
<!-- Define snippets -->
|
|
{#snippet name(param1, param2)}
|
|
<div>{param1}</div>
|
|
{/snippet}
|
|
```
|
|
|
|
---
|
|
|
|
## Component Patterns
|
|
|
|
```svelte
|
|
<!-- Basic Component -->
|
|
<script lang="ts">
|
|
import type { Snippet } from 'svelte';
|
|
|
|
interface Props {
|
|
title: string;
|
|
variant?: 'primary' | 'secondary';
|
|
disabled?: boolean;
|
|
onclick?: () => void;
|
|
children: Snippet;
|
|
}
|
|
|
|
let {
|
|
title,
|
|
variant = 'primary',
|
|
disabled = false,
|
|
onclick,
|
|
children
|
|
}: Props = $props();
|
|
</script>
|
|
|
|
<button
|
|
class="btn btn-{variant}"
|
|
{disabled}
|
|
{onclick}
|
|
>
|
|
{@render children()}
|
|
</button>
|
|
|
|
<!-- With Named Snippets -->
|
|
<script lang="ts">
|
|
import type { Snippet } from 'svelte';
|
|
|
|
interface Props {
|
|
header?: Snippet;
|
|
children?: Snippet;
|
|
footer?: Snippet<[{ close: () => void }]>;
|
|
}
|
|
|
|
let { header, children, footer }: Props = $props();
|
|
|
|
function close() {
|
|
// close logic
|
|
}
|
|
</script>
|
|
|
|
{@render header?.()}
|
|
{@render children?.()}
|
|
{@render footer?.({ close })}
|
|
```
|
|
|
|
---
|
|
|
|
## State Management
|
|
|
|
```typescript
|
|
// stores/counter.svelte.ts
|
|
export class CounterStore {
|
|
count = $state(0);
|
|
doubled = $derived(this.count * 2);
|
|
|
|
increment = () => { this.count++; }
|
|
decrement = () => { this.count--; }
|
|
reset = () => { this.count = 0; }
|
|
}
|
|
|
|
export const counter = new CounterStore();
|
|
|
|
// Usage in component
|
|
import { counter } from '$lib/stores/counter.svelte';
|
|
|
|
// Read: counter.count, counter.doubled
|
|
// Write: counter.increment()
|
|
```
|
|
|
|
---
|
|
|
|
## SvelteKit Essentials
|
|
|
|
```typescript
|
|
// +page.ts (Universal load)
|
|
export async function load({ params, fetch }) {
|
|
const data = await fetch(`/api/items/${params.id}`);
|
|
return { item: await data.json() };
|
|
}
|
|
|
|
// +page.server.ts (Server-only load)
|
|
export async function load({ params, locals }) {
|
|
return { user: locals.user };
|
|
}
|
|
|
|
// Form Actions
|
|
export const actions = {
|
|
default: async ({ request }) => {
|
|
const data = await request.formData();
|
|
// Process form
|
|
return { success: true };
|
|
},
|
|
named: async ({ request }) => {
|
|
// Named action: <form action="?/named">
|
|
}
|
|
};
|
|
|
|
// API Routes (+server.ts)
|
|
export async function GET({ params }) {
|
|
return json({ data: 'value' });
|
|
}
|
|
|
|
export async function POST({ request }) {
|
|
const body = await request.json();
|
|
return json({ created: true }, { status: 201 });
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## File Structure
|
|
|
|
```
|
|
src/
|
|
├── app.html # HTML template
|
|
├── app.d.ts # Type declarations
|
|
├── lib/
|
|
│ ├── components/ # Reusable components
|
|
│ ├── stores/ # State (.svelte.ts files)
|
|
│ ├── utils/ # Utility functions
|
|
│ ├── hooks/ # Custom hooks
|
|
│ └── server/ # Server-only code
|
|
├── routes/
|
|
│ ├── +page.svelte # Page component
|
|
│ ├── +page.ts # Page load (universal)
|
|
│ ├── +page.server.ts # Page load (server) + actions
|
|
│ ├── +layout.svelte # Layout component
|
|
│ ├── +layout.ts # Layout load
|
|
│ ├── +error.svelte # Error page
|
|
│ └── api/
|
|
│ └── +server.ts # API endpoint
|
|
└── hooks.server.ts # Server hooks
|
|
```
|
|
|
|
---
|
|
|
|
## Common Anti-Patterns
|
|
|
|
```svelte
|
|
<!-- ❌ DON'T: Destructure reactive state -->
|
|
let { name } = user; // Loses reactivity!
|
|
|
|
<!-- ✅ DO: Access properties directly -->
|
|
{user.name}
|
|
|
|
<!-- ❌ DON'T: Use $effect for derived values -->
|
|
$effect(() => {
|
|
doubled = count * 2; // Use $derived instead!
|
|
});
|
|
|
|
<!-- ✅ DO: Use $derived -->
|
|
let doubled = $derived(count * 2);
|
|
|
|
<!-- ❌ DON'T: Forget optional chaining for snippets -->
|
|
{@render children()} // May error if undefined
|
|
|
|
<!-- ✅ DO: Use optional chaining -->
|
|
{@render children?.()}
|
|
|
|
<!-- ❌ DON'T: Modify state in $derived -->
|
|
let bad = $derived(count++); // Side effect!
|
|
|
|
<!-- ✅ DO: Keep $derived pure -->
|
|
let good = $derived(count + 1);
|
|
```
|
|
|
|
---
|
|
|
|
## TypeScript Quick Types
|
|
|
|
```typescript
|
|
import type {
|
|
Snippet, // Snippet type
|
|
Component, // Component type
|
|
ComponentProps // Extract props from component
|
|
} from 'svelte';
|
|
|
|
import type {
|
|
PageData, // From +page.ts load
|
|
PageServerData, // From +page.server.ts load
|
|
Actions, // Form actions type
|
|
RequestHandler // API route handler
|
|
} from './$types';
|
|
|
|
// Snippet with params
|
|
type ItemSnippet = Snippet<[{ item: Item; index: number }]>;
|
|
|
|
// Component props
|
|
type ButtonProps = ComponentProps<typeof Button>;
|
|
```
|
|
|
|
---
|
|
|
|
## Key Commands
|
|
|
|
```bash
|
|
# Create new project
|
|
npx sv create my-app
|
|
|
|
# Development
|
|
npm run dev
|
|
|
|
# Build
|
|
npm run build
|
|
|
|
# Preview production build
|
|
npm run preview
|
|
|
|
# Type checking
|
|
npm run check
|
|
|
|
# Run migration
|
|
npx sv migrate svelte-5
|
|
```
|