mirror of https://github.com/sveltejs/svelte
docs: legacy docs (#13756)
* docs: legacy docs add docs on old syntax * rename section * tweaks * tweak * tweaks * tweaks * tweaks * fix link --------- Co-authored-by: Rich Harris <rich.harris@vercel.com>transition-out-effect-tracking
parent
8ff2af52d3
commit
4c7cfff434
@ -0,0 +1,12 @@
|
||||
---
|
||||
title: Overview
|
||||
---
|
||||
|
||||
Svelte 5 introduced some significant changes to Svelte's API, including [runes](what-are-runes), [snippets](snippet) and event attributes. As a result, some Svelte 3/4 features are deprecated (though supported for now, unless otherwise specified) and will eventually be removed. We recommend that you incrementally [migrate your existing code](v5-migration-guide).
|
||||
|
||||
The following pages document these features for
|
||||
|
||||
- people still using Svelte 3/4
|
||||
- people using Svelte 5, but with components that haven't yet been migrated
|
||||
|
||||
Since Svelte 3/4 syntax still works in Svelte 5, we will distinguish between _legacy mode_ and _runes mode_. Once a component is in runes mode (which you can opt into by using runes, or by explicitly setting the `runes: true` compiler option), legacy mode features are no longer available.
|
@ -0,0 +1,34 @@
|
||||
---
|
||||
title: Reactive let/var declarations
|
||||
---
|
||||
|
||||
In runes mode, reactive state is explicitly declared with the [`$state` rune]($state).
|
||||
|
||||
In legacy mode, variables declared at the top level of a component are automatically considered _reactive_. Reassigning or mutating these variables (`count += 1` or `object.x = y`) will cause the UI to update.
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
let count = 0;
|
||||
</script>
|
||||
|
||||
<button on:click={() => count += 1}>
|
||||
clicks: {count}
|
||||
</button>
|
||||
```
|
||||
|
||||
Because Svelte's legacy mode reactivity is based on _assignments_, using array methods like `.push()` and `.splice()` won't automatically trigger updates. A subsequent assignment is required to 'tell' the compiler to update the UI:
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
let numbers = [1, 2, 3, 4];
|
||||
|
||||
function addNumber() {
|
||||
// this method call does not trigger an update
|
||||
numbers.push(numbers.length + 1);
|
||||
|
||||
// this assignment will update anything
|
||||
// that depends on `numbers`
|
||||
numbers = numbers;
|
||||
}
|
||||
</script>
|
||||
```
|
@ -0,0 +1,87 @@
|
||||
---
|
||||
title: Reactive $: statements
|
||||
---
|
||||
|
||||
In runes mode, reactions to state updates are handled with the [`$derived`]($derived) and [`$effect`]($effect) runes.
|
||||
|
||||
In legacy mode, any top-level statement (i.e. not inside a block or a function) can be made reactive by prefixing it with a `$:` [label](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/label). These statements run after other code in the `<script>` and before the component markup is rendered, then whenever the values that they depend on change.
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
let a = 1;
|
||||
let b = 2;
|
||||
|
||||
// this is a 'reactive statement', and it will re-run
|
||||
// when `a`, `b` or `sum` change
|
||||
$: console.log(`${a} + ${b} = ${sum}`);
|
||||
|
||||
// this is a 'reactive assignment' — `sum` will be
|
||||
// recalculated when `a` or `b` change. It is
|
||||
// not necessary to declare `sum` separately
|
||||
$: sum = a + b;
|
||||
</script>
|
||||
```
|
||||
|
||||
Statements are ordered _topologically_ by their dependencies and their assignments: since the `console.log` statement depends on `sum`, `sum` is calculated first even though it appears later in the source.
|
||||
|
||||
Multiple statements can be combined by putting them in a block:
|
||||
|
||||
```js
|
||||
// @noErrors
|
||||
$: {
|
||||
// recalculate `total` when `items` changes
|
||||
total = 0;
|
||||
|
||||
for (const item of items) {
|
||||
total += item.value;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The left-hand side of a reactive assignments can be an identifier, or it can be a destructuring assignment:
|
||||
|
||||
```js
|
||||
// @noErrors
|
||||
$: ({ larry, moe, curly } = stooges);
|
||||
```
|
||||
|
||||
## Understanding dependencies
|
||||
|
||||
The dependencies of a `$:` statement are determined at compile time — they are whichever variables are referenced (but not assigned to) inside the statement.
|
||||
|
||||
In other words, a statement like this will _not_ re-run when `count` changes, because the compiler cannot 'see' the dependency:
|
||||
|
||||
```js
|
||||
// @noErrors
|
||||
let count = 0;
|
||||
let double = () => count * 2;
|
||||
|
||||
$: doubled = double();
|
||||
```
|
||||
|
||||
Similarly, topological ordering will fail if dependencies are referenced indirectly: `z` will never update, because `y` is not considered 'dirty' when the update occurs. Moving `$: z = y` below `$: setY(x)` will fix it:
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
let x = 0;
|
||||
let y = 0;
|
||||
|
||||
$: z = y;
|
||||
$: setY(x);
|
||||
|
||||
function setY(value) {
|
||||
y = value;
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
## Browser-only code
|
||||
|
||||
Reactive statements run during server-side rendering as well as in the browser. This means that any code that should only run in the browser must be wrapped in an `if` block:
|
||||
|
||||
```js
|
||||
// @noErrors
|
||||
$: if (browser) {
|
||||
document.title = title;
|
||||
}
|
||||
```
|
@ -0,0 +1,72 @@
|
||||
---
|
||||
title: export let
|
||||
---
|
||||
|
||||
In runes mode, [component props](basic-markup#Component-props) are declared with the [`$props`]($props) rune, allowing parent components to pass in data.
|
||||
|
||||
In legacy mode, props are marked with the `export` keyword, and can have a default value:
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
export let foo;
|
||||
export let bar = 'default value';
|
||||
|
||||
// Values that are passed in as props
|
||||
// are immediately available
|
||||
console.log({ foo });
|
||||
</script>
|
||||
```
|
||||
|
||||
The default value is used if it would otherwise be `undefined` when the component is created.
|
||||
|
||||
> [!NOTE] Unlike in runes mode, if the parent component changes a prop from a defined value to `undefined`, it does not revert to the initial value.
|
||||
|
||||
Props without default values are considered _required_, and Svelte will print a warning during development if no value is provided, which you can squelch by specifying `undefined` as the default value:
|
||||
|
||||
```js
|
||||
export let foo +++= undefined;+++
|
||||
```
|
||||
|
||||
## Component exports
|
||||
|
||||
An exported `const`, `class` or `function` declaration is _not_ considered a prop — instead, it becomes part of the component's API:
|
||||
|
||||
```svelte
|
||||
<!--- file: Greeter.svelte--->
|
||||
<script>
|
||||
export function greet(name) {
|
||||
alert(`hello ${name}!`);
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
```svelte
|
||||
<!--- file: App.svelte --->
|
||||
<script>
|
||||
import Greeter from './Greeter.svelte';
|
||||
|
||||
let greeter;
|
||||
</script>
|
||||
|
||||
<Greeter bind:this={greeter} />
|
||||
|
||||
<button on:click={() => greeter.greet('world')}>
|
||||
greet
|
||||
</button>
|
||||
```
|
||||
|
||||
## Renaming props
|
||||
|
||||
The `export` keyword can appear separately from the declaration. This is useful for renaming props, for example in the case of a reserved word:
|
||||
|
||||
```svelte
|
||||
<!--- file: App.svelte --->
|
||||
<script>
|
||||
/** @type {string} */
|
||||
let className;
|
||||
|
||||
// creates a `class` property, even
|
||||
// though it is a reserved word
|
||||
export { className as class };
|
||||
</script>
|
||||
```
|
@ -0,0 +1,30 @@
|
||||
---
|
||||
title: $$props and $$restProps
|
||||
---
|
||||
|
||||
In runes mode, getting an object containing all the props that were passed in is easy, using the [`$props`]($props) rune.
|
||||
|
||||
In legacy mode, we use `$$props` and `$$restProps`:
|
||||
|
||||
- `$$props` contains all the props that were passed in, including ones that are not individually declared with the `export` keyword
|
||||
- `$$restProps` contains all the props that were passed in _except_ the ones that were individually declared
|
||||
|
||||
For example, a `<Button>` component might need to pass along all its props to its own `<button>` element, except the `variant` prop:
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
export let variant;
|
||||
</script>
|
||||
|
||||
<button {...$$restProps} class="variant-{variant} {$$props.class ?? ''}">
|
||||
click me
|
||||
</button>
|
||||
|
||||
<style>
|
||||
.variant-danger {
|
||||
background: red;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
In Svelte 3/4 using `$$props` and `$$restProps` creates a modest performance penalty, so they should only be used when needed.
|
@ -0,0 +1,136 @@
|
||||
---
|
||||
title: on:
|
||||
---
|
||||
|
||||
In runes mode, event handlers are just like any other attribute or prop.
|
||||
|
||||
In legacy mode, we use the `on:` directive:
|
||||
|
||||
```svelte
|
||||
<!--- file: App.svelte --->
|
||||
<script>
|
||||
let count = 0;
|
||||
|
||||
/** @param {MouseEvent} event */
|
||||
function handleClick(event) {
|
||||
count += 1;
|
||||
}
|
||||
</script>
|
||||
|
||||
<button on:click={handleClick}>
|
||||
count: {count}
|
||||
</button>
|
||||
```
|
||||
|
||||
Handlers can be declared inline with no performance penalty:
|
||||
|
||||
```svelte
|
||||
<button on:click={() => (count += 1)}>
|
||||
count: {count}
|
||||
</button>
|
||||
```
|
||||
|
||||
Add _modifiers_ to element event handlers with the `|` character.
|
||||
|
||||
```svelte
|
||||
<form on:submit|preventDefault={handleSubmit}>
|
||||
<!-- the `submit` event's default is prevented,
|
||||
so the page won't reload -->
|
||||
</form>
|
||||
```
|
||||
|
||||
The following modifiers are available:
|
||||
|
||||
- `preventDefault` — calls `event.preventDefault()` before running the handler
|
||||
- `stopPropagation` — calls `event.stopPropagation()`, preventing the event reaching the next element
|
||||
- `stopImmediatePropagation` - calls `event.stopImmediatePropagation()`, preventing other listeners of the same event from being fired.
|
||||
- `passive` — improves scrolling performance on touch/wheel events (Svelte will add it automatically where it's safe to do so)
|
||||
- `nonpassive` — explicitly set `passive: false`
|
||||
- `capture` — fires the handler during the _capture_ phase instead of the _bubbling_ phase
|
||||
- `once` — remove the handler after the first time it runs
|
||||
- `self` — only trigger handler if `event.target` is the element itself
|
||||
- `trusted` — only trigger handler if `event.isTrusted` is `true`. I.e. if the event is triggered by a user action.
|
||||
|
||||
Modifiers can be chained together, e.g. `on:click|once|capture={...}`.
|
||||
|
||||
If the `on:` directive is used without a value, the component will _forward_ the event, meaning that a consumer of the component can listen for it.
|
||||
|
||||
```svelte
|
||||
<button on:click>
|
||||
The component itself will emit the click event
|
||||
</button>
|
||||
```
|
||||
|
||||
It's possible to have multiple event listeners for the same event:
|
||||
|
||||
```svelte
|
||||
<!--- file: App.svelte --->
|
||||
<script>
|
||||
let count = 0;
|
||||
|
||||
function increment() {
|
||||
count += 1;
|
||||
}
|
||||
|
||||
/** @param {MouseEvent} event */
|
||||
function log(event) {
|
||||
console.log(event);
|
||||
}
|
||||
</script>
|
||||
|
||||
<button on:click={increment} on:click={log}>
|
||||
clicks: {count}
|
||||
</button>
|
||||
```
|
||||
|
||||
## Component events
|
||||
|
||||
Components can dispatch events by creating a _dispatcher_ when they are initialised:
|
||||
|
||||
```svelte
|
||||
<!--- file: Stepper.svelte -->
|
||||
<script>
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
const dispatch = createEventDispatcher();
|
||||
</script>
|
||||
|
||||
<button on:click={() => dispatch('decrement')}>decrement</button>
|
||||
<button on:click={() => dispatch('increment')}>increment</button>
|
||||
```
|
||||
|
||||
`dispatch` creates a [`CustomEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent). If a second argument is provided, it becomes the `detail` property of the event object.
|
||||
|
||||
A consumer of this component can listen for the dispatched events:
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import Stepper from './Stepper.svelte';
|
||||
|
||||
let n = 0;
|
||||
</script>
|
||||
|
||||
<Stepper
|
||||
on:decrement={() => n -= 1}
|
||||
on:increment={() => n += 1}
|
||||
/>
|
||||
|
||||
<p>n: {n}</p>
|
||||
```
|
||||
|
||||
Component events do not bubble — a parent component can only listen for events on its immediate children.
|
||||
|
||||
Other than `once`, modifiers are not valid on component event handlers.
|
||||
|
||||
> [!NOTE]
|
||||
> If you're planning an eventual migration to Svelte 5, use callback props instead. This will make upgrading easier as `createEventDispatcher` is deprecated:
|
||||
>
|
||||
> ```svelte
|
||||
> <!--- file: Stepper.svelte --->
|
||||
> <script>
|
||||
> export let decrement;
|
||||
> export let increment;
|
||||
> </script>
|
||||
>
|
||||
> <button on:click={decrement}>decrement</button>
|
||||
> <button on:click={increment}>increment</button>
|
||||
> ```
|
@ -0,0 +1,116 @@
|
||||
---
|
||||
title: <slot>
|
||||
---
|
||||
|
||||
In Svelte 5, content can be passed to components in the form of [snippets](snippet) and rendered using [render tags](@render).
|
||||
|
||||
In legacy mode, content inside component tags is considered _slotted content_, which can be rendered by the component using a `<slot>` element:
|
||||
|
||||
```svelte
|
||||
<!--- file: App.svelte --->
|
||||
<script>
|
||||
import Modal from './Modal.svelte';
|
||||
</script>
|
||||
|
||||
<Modal>This is some slotted content</Modal>
|
||||
```
|
||||
|
||||
```svelte
|
||||
<!--- file: Modal.svelte --->
|
||||
<div class="modal">
|
||||
<slot></slot>
|
||||
</div>
|
||||
```
|
||||
|
||||
> [!NOTE] If you want to render a regular `<slot>` element, you can use `<svelte:element this={'slot'} />`.
|
||||
|
||||
## Named slots
|
||||
|
||||
A component can have _named_ slots in addition to the default slot. On the parent side, add a `slot="..."` attribute to an element, component or [`<svelte:fragment>`](legacy-svelte-fragment) directly inside the component tags.
|
||||
|
||||
```svelte
|
||||
<!--- file: App.svelte --->
|
||||
<script>
|
||||
import Modal from './Modal.svelte';
|
||||
|
||||
let open = true;
|
||||
</script>
|
||||
|
||||
{#if open}
|
||||
<Modal>
|
||||
This is some slotted content
|
||||
|
||||
+++<div slot="buttons">+++
|
||||
<button on:click={() => open = false}>
|
||||
close
|
||||
</button>
|
||||
+++</div>+++
|
||||
</Modal>
|
||||
{/if}
|
||||
```
|
||||
|
||||
On the child side, add a corresponding `<slot name="...">` element:
|
||||
|
||||
```svelte
|
||||
<!--- file: Modal.svelte --->
|
||||
<div class="modal">
|
||||
<slot></slot>
|
||||
<hr>
|
||||
+++<slot name="buttons"></slot>+++
|
||||
</div>
|
||||
```
|
||||
|
||||
## Fallback content
|
||||
|
||||
If no slotted content is provided, a component can define fallback content by putting it inside the `<slot>` element:
|
||||
|
||||
```svelte
|
||||
<slot>
|
||||
This will be rendered if no slotted content is provided
|
||||
</slot>
|
||||
```
|
||||
|
||||
## Passing data to slotted content
|
||||
|
||||
Slots can be rendered zero or more times and can pass values _back_ to the parent using props. The parent exposes the values to the slot template using the `let:` directive.
|
||||
|
||||
The usual shorthand rules apply — `let:item` is equivalent to `let:item={item}`, and `<slot {item}>` is equivalent to `<slot item={item}>`.
|
||||
|
||||
```svelte
|
||||
<!-- FancyList.svelte -->
|
||||
<ul>
|
||||
{#each items as item}
|
||||
<li class="fancy">
|
||||
<slot prop={item} />
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
|
||||
<!-- App.svelte -->
|
||||
<FancyList {items} let:prop={thing}>
|
||||
<div>{thing.text}</div>
|
||||
</FancyList>
|
||||
```
|
||||
|
||||
Named slots can also expose values. The `let:` directive goes on the element with the `slot` attribute.
|
||||
|
||||
```svelte
|
||||
<!-- FancyList.svelte -->
|
||||
<ul>
|
||||
{#each items as item}
|
||||
<li class="fancy">
|
||||
<slot name="item" {item} />
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
|
||||
<slot name="footer" />
|
||||
|
||||
<!-- App.svelte -->
|
||||
<FancyList {items}>
|
||||
<div slot="item" let:item>{item.text}</div>
|
||||
<p slot="footer">Copyright (c) 2019 Svelte Industries</p>
|
||||
</FancyList>
|
||||
```
|
||||
|
||||
|
@ -0,0 +1,27 @@
|
||||
---
|
||||
title: $$slots
|
||||
---
|
||||
|
||||
In runes mode, we know which [snippets](snippet) were provided to a component, as they're just normal props.
|
||||
|
||||
In legacy mode, the way to know if content was provided for a given slot is with the `$$slots` object, whose keys are the names of the slots passed into the component by the parent.
|
||||
|
||||
```svelte
|
||||
<!--- file: Card.svelte --->
|
||||
<div>
|
||||
<slot name="title" />
|
||||
{#if $$slots.description}
|
||||
<!-- This <hr> and slot will render only if `slot="description"` is provided. -->
|
||||
<hr />
|
||||
<slot name="description" />
|
||||
{/if}
|
||||
</div>
|
||||
```
|
||||
|
||||
```svelte
|
||||
<!--- file: App.svelte --->
|
||||
<Card>
|
||||
<h1 slot="title">Blog Post Title</h1>
|
||||
<!-- No slot named "description" was provided so the optional slot will not be rendered. -->
|
||||
</Card>
|
||||
```
|
@ -0,0 +1,32 @@
|
||||
---
|
||||
title: <svelte:fragment>
|
||||
---
|
||||
|
||||
The `<svelte:fragment>` element allows you to place content in a [named slot](/docs/special-elements#slot-slot-name-name) without wrapping it in a container DOM element. This keeps the flow layout of your document intact.
|
||||
|
||||
```svelte
|
||||
<!--- file: Widget.svelte --->
|
||||
<div>
|
||||
<slot name="header">No header was provided</slot>
|
||||
<p>Some content between header and footer</p>
|
||||
<slot name="footer" />
|
||||
</div>
|
||||
```
|
||||
|
||||
```svelte
|
||||
<!--- file: App.svelte --->
|
||||
<script>
|
||||
import Widget from './Widget.svelte';
|
||||
</script>
|
||||
|
||||
<Widget>
|
||||
<h1 slot="header">Hello</h1>
|
||||
<svelte:fragment slot="footer">
|
||||
<p>All rights reserved.</p>
|
||||
<p>Copyright (c) 2019 Svelte Industries</p>
|
||||
</svelte:fragment>
|
||||
</Widget>
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> In Svelte 5+, this concept is obsolete, as snippets don't create a wrapping element
|
@ -0,0 +1,13 @@
|
||||
---
|
||||
title: <svelte:component>
|
||||
---
|
||||
|
||||
In runes mode, `<MyComponent>` will re-render if the value of `MyComponent` changes.
|
||||
|
||||
In legacy mode, it won't — we must use `<svelte:component>`, which destroys and recreates the component instance when the value of its `this` expression changes:
|
||||
|
||||
```svelte
|
||||
<svelte:component this={MyComponent} />
|
||||
```
|
||||
|
||||
If `this` is falsy, no component is rendered.
|
@ -0,0 +1,37 @@
|
||||
---
|
||||
title: <svelte:self>
|
||||
---
|
||||
|
||||
The `<svelte:self>` element allows a component to include itself, recursively.
|
||||
|
||||
It cannot appear at the top level of your markup; it must be inside an if or each block or passed to a component's slot to prevent an infinite loop.
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
export let count;
|
||||
</script>
|
||||
|
||||
{#if count > 0}
|
||||
<p>counting down... {count}</p>
|
||||
<svelte:self count={count - 1} />
|
||||
{:else}
|
||||
<p>lift-off!</p>
|
||||
{/if}
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> This concept is obsolete, as components can import themselves:
|
||||
> ```svelte
|
||||
> <!--- file: App.svelte --->
|
||||
> <script>
|
||||
> import Self from './App.svelte'
|
||||
> export let count;
|
||||
> </script>
|
||||
>
|
||||
> {#if count > 0}
|
||||
> <p>counting down... {count}</p>
|
||||
> <Self count={count - 1} />
|
||||
> {:else}
|
||||
> <p>lift-off!</p>
|
||||
> {/if}
|
||||
> ```
|
@ -0,0 +1,3 @@
|
||||
---
|
||||
title: Legacy APIs
|
||||
---
|
Loading…
Reference in new issue