mirror of https://github.com/sveltejs/svelte
Add a tutorial (#2132)
parent
bcedaea620
commit
4541d58798
@ -0,0 +1 @@
|
||||
<h1>Hello world!</h1>
|
@ -0,0 +1,32 @@
|
||||
---
|
||||
title: Basics
|
||||
---
|
||||
|
||||
Welcome to the Svelte tutorial. This will teach you everything you need to know to build fast, small web applications easily.
|
||||
|
||||
You can also consult the [API docs](docs) and the [examples](examples), or — if you're impatient to start hacking on your machine locally — the [60-second quickstart](blog/the-easiest-way-to-get-started).
|
||||
|
||||
|
||||
## What is Svelte?
|
||||
|
||||
Svelte is a tool for building fast web applications.
|
||||
|
||||
It is similar to JavaScript frameworks such as React and Vue, which share a goal of making it easy to build slick interactive user interfaces.
|
||||
|
||||
But there's a crucial difference: Svelte converts your app into ideal JavaScript at *build time*, rather than interpreting your application code at *run time*. This means you don't pay the performance cost of the framework's abstractions, and you don't incur a penalty when your app first loads.
|
||||
|
||||
You can build your entire app with Svelte, or you can add it incrementally to an existing codebase. You can also ship components as standalone packages that work anywhere, without the overhead of a dependency on a conventional framework.
|
||||
|
||||
|
||||
## How to use this tutorial
|
||||
|
||||
You'll need to have basic familiarity with HTML, CSS and JavaScript to understand Svelte.
|
||||
|
||||
As you progress through the tutorial, you'll be presented with mini exercises designed to illustrate new features. Later chapters build on the knowledge gained in earlier ones, so it's recommended that you go from start to finish. If necessary, you can navigate via the dropdown above (click 'Introduction / Basics').
|
||||
|
||||
Each tutorial chapter will have a 'Show me' button that you can click if you get stuck following the instructions. Try not to rely on it too much; you will learn faster by figuring out where to put each suggested code block and manually typing it in to the editor.
|
||||
|
||||
|
||||
## Understanding components
|
||||
|
||||
In Svelte, an application is composed from one or more *components*. A component is a reusable self-contained block of code that encapsulates HTML, CSS and JavaScript that belong together, written into a `.svelte` file. The 'hello world' example on the right is a simple component.
|
@ -0,0 +1 @@
|
||||
<h1>Hello world!</h1>
|
@ -0,0 +1,5 @@
|
||||
<script>
|
||||
let name = 'world';
|
||||
</script>
|
||||
|
||||
<h1>Hello {name.toUpperCase()}!</h1>
|
@ -0,0 +1,23 @@
|
||||
---
|
||||
title: Adding data
|
||||
---
|
||||
|
||||
A component that just renders some static markup isn't very interesting. Let's add some data.
|
||||
|
||||
First, add a script tag to your component and declare a `name` variable:
|
||||
|
||||
```html
|
||||
<script>
|
||||
let name = 'world';
|
||||
</script>
|
||||
|
||||
<h1>Hello world!</h1>
|
||||
```
|
||||
|
||||
Then, we can refer to `name` in the markup:
|
||||
|
||||
```html
|
||||
<h1>Hello {name}!</h1>
|
||||
```
|
||||
|
||||
Inside the curly braces, we can put any JavaScript we want. Try changing `name` to `name.toUpperCase()` for a shoutier greeting.
|
@ -0,0 +1,5 @@
|
||||
<script>
|
||||
let src = 'tutorial/image.gif';
|
||||
</script>
|
||||
|
||||
<img>
|
@ -0,0 +1,6 @@
|
||||
<script>
|
||||
let src = 'tutorial/image.gif';
|
||||
let name = 'Rick Astley';
|
||||
</script>
|
||||
|
||||
<img {src} alt="{name} dancing">
|
@ -0,0 +1,35 @@
|
||||
---
|
||||
title: Dynamic attributes
|
||||
---
|
||||
|
||||
Just like you can use curly braces to control text, you can use them to control element attributes.
|
||||
|
||||
Our image is missing a `src` — let's add one:
|
||||
|
||||
```html
|
||||
<img src={src}>
|
||||
```
|
||||
|
||||
That's better. But Svelte is giving us a warning:
|
||||
|
||||
> A11y: <img> element should have an alt attribute
|
||||
|
||||
When building web apps, it's important to make sure that they're *accessible* to the broadest possible userbase, including people with (for example) impaired vision or motion, or people without powerful hardware or good internet connections. Accessibility (shortened to a11y) isn't always easy to get right, but Svelte will help by warning you if you write inaccessible markup.
|
||||
|
||||
In this case, we're missing the `alt` tag that describes the image for people using screenreaders, or people with slow or flaky internet connections that can't download the image. Let's add one:
|
||||
|
||||
```html
|
||||
<img src={src} alt="A man dancing">
|
||||
```
|
||||
|
||||
We can use curly braces *inside* attributes. Try changing it to `"{name} dancing"` — remember to declare a `name` variable in the `<script>` block.
|
||||
|
||||
|
||||
## Shorthand attributes
|
||||
|
||||
It's not uncommon to have an attribute where the name and value are the same, like `src={src}`. Svelte gives us a convenient shorthand for these cases:
|
||||
|
||||
```html
|
||||
<img {src} alt="...">
|
||||
```
|
||||
|
@ -0,0 +1,5 @@
|
||||
<style>
|
||||
/* styles goes here */
|
||||
</style>
|
||||
|
||||
<p>This is a paragraph.</p>
|
@ -0,0 +1,9 @@
|
||||
<style>
|
||||
p {
|
||||
color: purple;
|
||||
font-family: 'Comic Sans MS';
|
||||
font-size: 2em;
|
||||
}
|
||||
</style>
|
||||
|
||||
<p>This is a paragraph.</p>
|
@ -0,0 +1,19 @@
|
||||
---
|
||||
title: Styling
|
||||
---
|
||||
|
||||
Just like in HTML, you can add a `<style>` tag to your component. Let's add some styles to the `<p>` element:
|
||||
|
||||
```html
|
||||
<style>
|
||||
p {
|
||||
color: purple;
|
||||
font-family: 'Comic Sans MS';
|
||||
font-size: 2em;
|
||||
}
|
||||
</style>
|
||||
|
||||
<p>This is a paragraph.</p>
|
||||
```
|
||||
|
||||
Importantly, these rules are *scoped to the component*. You won't accidentally change the style of `<p>` elements elsewhere in your app, as we'll see in the next step.
|
@ -0,0 +1,9 @@
|
||||
<style>
|
||||
p {
|
||||
color: purple;
|
||||
font-family: 'Comic Sans MS';
|
||||
font-size: 2em;
|
||||
}
|
||||
</style>
|
||||
|
||||
<p>This is a paragraph.</p>
|
@ -0,0 +1 @@
|
||||
<p>This is another paragraph.</p>
|
@ -0,0 +1,14 @@
|
||||
<script>
|
||||
import Nested from './Nested.svelte';
|
||||
</script>
|
||||
|
||||
<style>
|
||||
p {
|
||||
color: purple;
|
||||
font-family: 'Comic Sans MS';
|
||||
font-size: 2em;
|
||||
}
|
||||
</style>
|
||||
|
||||
<p>This is a paragraph.</p>
|
||||
<Nested/>
|
@ -0,0 +1 @@
|
||||
<p>This is another paragraph.</p>
|
@ -0,0 +1,22 @@
|
||||
---
|
||||
title: Nested components
|
||||
---
|
||||
|
||||
It would be impractical to put your entire app in a single component. Instead, we can import components from other files and include them as though we were including elements.
|
||||
|
||||
Add a `<script>` tag that imports `Nested.svelte`...
|
||||
|
||||
```html
|
||||
<script>
|
||||
import Nested from './Nested.svelte';
|
||||
</script>
|
||||
```
|
||||
|
||||
...then add it to the markup:
|
||||
|
||||
```html
|
||||
<p>This is a paragraph.</p>
|
||||
<Nested/>
|
||||
```
|
||||
|
||||
Notice that even though `Nested.svelte` has a `<p>` element, the styles from `App.svelte` don't leak in.
|
@ -0,0 +1,5 @@
|
||||
<script>
|
||||
let string = `this string contains some <strong>HTML!!!</strong>`;
|
||||
</script>
|
||||
|
||||
<p>{string}</p>
|
@ -0,0 +1,5 @@
|
||||
<script>
|
||||
let string = `this string contains some <strong>HTML!!!</strong>`;
|
||||
</script>
|
||||
|
||||
<p>{@html string}</p>
|
@ -0,0 +1,15 @@
|
||||
---
|
||||
title: HTML tags
|
||||
---
|
||||
|
||||
Ordinarily, strings are inserted as plain text, meaning that characters like `<` and `>` have no special meaning.
|
||||
|
||||
But sometimes you need to render HTML directly into a component. For example, the words you're reading right now exist in a markdown file that gets included on this page as a blob of HTML.
|
||||
|
||||
In Svelte, you do this with the special `{@html ...}` tag:
|
||||
|
||||
```html
|
||||
<p>{@html string}</p>
|
||||
```
|
||||
|
||||
> Svelte doesn't perform any sanitization of the data before it gets inserted into the DOM. In other words, it's critical that you manually escape HTML that comes from sources you don't trust, otherwise you risk exposing your users to XSS attacks.
|
@ -0,0 +1 @@
|
||||
<h1>What now?</h1>
|
@ -0,0 +1,31 @@
|
||||
---
|
||||
title: Making an app
|
||||
---
|
||||
|
||||
This tutorial is designed to get you familiar with the process of writing components. But at some point, you'll want to start writing components in the comfort of your own text editor.
|
||||
|
||||
First, you'll need to integrate Svelte with a build tool. Popular choices are:
|
||||
|
||||
* [Rollup](https://rollupjs.org) / [rollup-plugin-svelte](https://github.com/rollup/rollup-plugin-svelte)
|
||||
* [webpack](https://webpack.js.org/) / [svelte-loader](https://github.com/sveltejs/svelte-loader)
|
||||
* [Parcel](https://parceljs.org/) / [parcel-plugin-svelte](https://github.com/DeMoorJasper/parcel-plugin-svelte)
|
||||
|
||||
Don't worry if you're relatively new to web development and haven't used these tools before. We've prepared a simple step-by-step guide, [Svelte for new developers](blog/svelte-for-new-developers), which walks you through the process.
|
||||
|
||||
You'll also want to configure your text editor to treat `.svelte` files the same as `.html` for the sake of syntax highlighting. [Read this guide to learn how](blog/setting-up-your-editor).
|
||||
|
||||
Then, once you've got your project set up, using Svelte components is easy. The compiler turns each component into a regular JavaScript class — just import it and instantiate with `new`:
|
||||
|
||||
```js
|
||||
import App from './App.svelte';
|
||||
|
||||
const app = new App({
|
||||
target: document.body,
|
||||
props: {
|
||||
// we'll learn about props later
|
||||
answer: 42
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
You can then interact with `app` using the [component API](docs/component-api) if you need to.
|
@ -0,0 +1,3 @@
|
||||
{
|
||||
"title": "Introduction"
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
<script>
|
||||
let count = 0;
|
||||
|
||||
function handleClick() {
|
||||
// event handler code goes here
|
||||
}
|
||||
</script>
|
||||
|
||||
<button>
|
||||
Clicked {count} {count === 1 ? 'time' : 'times'}
|
||||
</button>
|
@ -0,0 +1,11 @@
|
||||
<script>
|
||||
let count = 0;
|
||||
|
||||
function handleClick() {
|
||||
count += 1;
|
||||
}
|
||||
</script>
|
||||
|
||||
<button on:click={handleClick}>
|
||||
Clicked {count} {count === 1 ? 'time' : 'times'}
|
||||
</button>
|
@ -0,0 +1,21 @@
|
||||
---
|
||||
title: Assignments
|
||||
---
|
||||
|
||||
At the heart of Svelte is a powerful system of *reactivity* for keeping the DOM in sync with your application state — for example, in response to an event.
|
||||
|
||||
To demonstrate it, we first need to wire up an event handler. Replace line 9 with this:
|
||||
|
||||
```html
|
||||
<button on:click={handleClick}>
|
||||
```
|
||||
|
||||
Inside the `handleClick` function, all we need to do is change the value of `count`:
|
||||
|
||||
```js
|
||||
function handleClick() {
|
||||
count += 1;
|
||||
}
|
||||
```
|
||||
|
||||
Svelte 'instruments' this assignment with some code that tells it the DOM will need to be updated.
|
@ -0,0 +1,11 @@
|
||||
<script>
|
||||
let count = 0;
|
||||
|
||||
function handleClick() {
|
||||
count += 1;
|
||||
}
|
||||
</script>
|
||||
|
||||
<button on:click={handleClick}>
|
||||
Clicked {count} {count === 1 ? 'time' : 'times'}
|
||||
</button>
|
@ -0,0 +1,14 @@
|
||||
<script>
|
||||
let count = 0;
|
||||
$: doubled = count * 2;
|
||||
|
||||
function handleClick() {
|
||||
count += 1;
|
||||
}
|
||||
</script>
|
||||
|
||||
<button on:click={handleClick}>
|
||||
Clicked {count} {count === 1 ? 'time' : 'times'}
|
||||
</button>
|
||||
|
||||
<p>{count} doubled is {doubled}</p>
|
@ -0,0 +1,22 @@
|
||||
---
|
||||
title: Declarations
|
||||
---
|
||||
|
||||
Svelte automatically updates the DOM when your component's state changes. Often, some parts of a component's state need to be computed from *other* parts (such as a `fullname` derived from a `firstname` and a `lastname`), and recomputed whenever they change.
|
||||
|
||||
For these, we have *reactive declarations*. They look like this:
|
||||
|
||||
```js
|
||||
let count = 0;
|
||||
$: doubled = count * 2;
|
||||
```
|
||||
|
||||
> Don't worry if this looks a little alien. It's valid (if unconventional) JavaScript, which Svelte interprets to mean 're-run this code whenever any of the referenced values change'. Once you get used to it, there's no going back.
|
||||
|
||||
Let's use `doubled` in our markup:
|
||||
|
||||
```html
|
||||
<p>{count} doubled is {doubled}</p>
|
||||
```
|
||||
|
||||
Of course, you could just write `{count * 2}` in the markup instead — you don't have to use reactive values. Reactive values become particularly valuable when you need to reference them multiple times, or you have values that depend on *other* reactive values.
|
@ -0,0 +1,11 @@
|
||||
<script>
|
||||
let count = 0;
|
||||
|
||||
function handleClick() {
|
||||
count += 1;
|
||||
}
|
||||
</script>
|
||||
|
||||
<button on:click={handleClick}>
|
||||
Clicked {count} {count === 1 ? 'time' : 'times'}
|
||||
</button>
|
@ -0,0 +1,16 @@
|
||||
<script>
|
||||
let count = 0;
|
||||
|
||||
$: if (count >= 10) {
|
||||
alert(`count is dangerously high!`);
|
||||
count = 9;
|
||||
}
|
||||
|
||||
function handleClick() {
|
||||
count += 1;
|
||||
}
|
||||
</script>
|
||||
|
||||
<button on:click={handleClick}>
|
||||
Clicked {count} {count === 1 ? 'time' : 'times'}
|
||||
</button>
|
@ -0,0 +1,27 @@
|
||||
---
|
||||
title: Statements
|
||||
---
|
||||
|
||||
We're not limited to declaring reactive *values* — we can also run arbitrary *statements* reactively. For example, we can log the value of `count` whenever it changes:
|
||||
|
||||
```js
|
||||
$: console.log(`the count is ${count}`);
|
||||
```
|
||||
|
||||
You can easily group statements together with a block:
|
||||
|
||||
```js
|
||||
$: {
|
||||
console.log(`the count is ${count}`);
|
||||
alert(`I SAID THE COUNT IS ${count}`);
|
||||
}
|
||||
```
|
||||
|
||||
You can even put the `$:` in front of things like `if` blocks:
|
||||
|
||||
```js
|
||||
$: if (count >= 10) {
|
||||
alert(`count is dangerously high!`);
|
||||
count = 9;
|
||||
}
|
||||
```
|
@ -0,0 +1,3 @@
|
||||
{
|
||||
"title": "Reactivity"
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
<script>
|
||||
import Nested from './Nested.svelte';
|
||||
</script>
|
||||
|
||||
<Nested answer={42}/>
|
@ -0,0 +1,5 @@
|
||||
<script>
|
||||
let answer;
|
||||
</script>
|
||||
|
||||
<p>The answer is {answer}</p>
|
@ -0,0 +1,5 @@
|
||||
<script>
|
||||
import Nested from './Nested.svelte';
|
||||
</script>
|
||||
|
||||
<Nested answer={42}/>
|
@ -0,0 +1,5 @@
|
||||
<script>
|
||||
export let answer;
|
||||
</script>
|
||||
|
||||
<p>The answer is {answer}</p>
|
@ -0,0 +1,15 @@
|
||||
---
|
||||
title: Declaring props
|
||||
---
|
||||
|
||||
So far, we've dealt exclusively with internal state — that is to say, the values are only accessible within a given component.
|
||||
|
||||
In any real application, you'll need to pass data from one component down to its children. To do that, we need to declare *properties*, generally shortened to 'props'. In Svelte, we do that with the `export` keyword. Edit the `Nested.svelte` component:
|
||||
|
||||
```html
|
||||
<script>
|
||||
export let answer;
|
||||
</script>
|
||||
```
|
||||
|
||||
> Just like `$:`, this may feel a little weird at first. That's not how `export` normally works in JavaScript modules! Just roll with it for now — it'll soon become second nature.
|
@ -0,0 +1,5 @@
|
||||
<script>
|
||||
import Nested from './Nested.svelte';
|
||||
</script>
|
||||
|
||||
<Nested answer={42}/>
|
@ -0,0 +1,5 @@
|
||||
<script>
|
||||
export let answer;
|
||||
</script>
|
||||
|
||||
<p>The answer is {answer}</p>
|
@ -0,0 +1,6 @@
|
||||
<script>
|
||||
import Nested from './Nested.svelte';
|
||||
</script>
|
||||
|
||||
<Nested answer={42}/>
|
||||
<Nested/>
|
@ -0,0 +1,5 @@
|
||||
<script>
|
||||
export let answer = 'a mystery';
|
||||
</script>
|
||||
|
||||
<p>The answer is {answer}</p>
|
@ -0,0 +1,18 @@
|
||||
---
|
||||
title: Default values
|
||||
---
|
||||
|
||||
We can easily specify default values for props:
|
||||
|
||||
```html
|
||||
<script>
|
||||
export let answer = 'a mystery';
|
||||
</script>
|
||||
```
|
||||
|
||||
If we now instantiate the component without an `answer` prop, it will fall back to the default:
|
||||
|
||||
```html
|
||||
<Nested answer={42}/>
|
||||
<Nested/>
|
||||
```
|
@ -0,0 +1,12 @@
|
||||
<script>
|
||||
import Info from './Info.svelte';
|
||||
|
||||
const pkg = {
|
||||
name: 'svelte',
|
||||
version: 3,
|
||||
speed: 'blazing',
|
||||
website: 'https://svelte.technology'
|
||||
};
|
||||
</script>
|
||||
|
||||
<Info name={pkg.name} version={pkg.version} speed={pkg.speed} website={pkg.website}/>
|
@ -0,0 +1,12 @@
|
||||
<script>
|
||||
export let name;
|
||||
export let version;
|
||||
export let speed;
|
||||
export let website;
|
||||
</script>
|
||||
|
||||
<p>
|
||||
The <code>{name}</code> package is {speed} fast.
|
||||
Download version {version} from <a href="https://www.npmjs.com/package/{name}">npm</a>
|
||||
and <a href={website}>learn more here</a>
|
||||
</p>
|
@ -0,0 +1,12 @@
|
||||
<script>
|
||||
import Info from './Info.svelte';
|
||||
|
||||
const pkg = {
|
||||
name: 'svelte',
|
||||
version: 3,
|
||||
speed: 'blazing',
|
||||
website: 'https://svelte.technology'
|
||||
};
|
||||
</script>
|
||||
|
||||
<Info {...pkg}/>
|
@ -0,0 +1,12 @@
|
||||
<script>
|
||||
export let name;
|
||||
export let version;
|
||||
export let speed;
|
||||
export let website;
|
||||
</script>
|
||||
|
||||
<p>
|
||||
The <code>{name}</code> package is {speed} fast.
|
||||
Download version {version} from <a href="https://www.npmjs.com/package/{name}">npm</a>
|
||||
and <a href={website}>learn more here</a>
|
||||
</p>
|
@ -0,0 +1,11 @@
|
||||
---
|
||||
title: Spread props
|
||||
---
|
||||
|
||||
If you have an object of properties, you can 'spread' them on to a component instead of specifying each one:
|
||||
|
||||
```html
|
||||
<Info {...pkg}/>
|
||||
```
|
||||
|
||||
> Conversely, if you need to reference all the props that were passed into a component, including ones that weren't declared with `export`, you can do so by accessing `$$props` directly. It's not generally recommended, as it's difficult for Svelte to optimise, but it's useful in rare cases.
|
@ -0,0 +1,3 @@
|
||||
{
|
||||
"title": "Props"
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
<script>
|
||||
let user = { loggedIn: false };
|
||||
|
||||
function toggle() {
|
||||
user.loggedIn = !user.loggedIn;
|
||||
}
|
||||
</script>
|
||||
|
||||
<button on:click={toggle}>
|
||||
Log out
|
||||
</button>
|
||||
|
||||
<button on:click={toggle}>
|
||||
Log in
|
||||
</button>
|
@ -0,0 +1,19 @@
|
||||
<script>
|
||||
let user = { loggedIn: false };
|
||||
|
||||
function toggle() {
|
||||
user.loggedIn = !user.loggedIn;
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if user.loggedIn}
|
||||
<button on:click={toggle}>
|
||||
Log out
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
{#if !user.loggedIn}
|
||||
<button on:click={toggle}>
|
||||
Log in
|
||||
</button>
|
||||
{/if}
|
@ -0,0 +1,23 @@
|
||||
---
|
||||
title: If blocks
|
||||
---
|
||||
|
||||
HTML doesn't have a way of expressing *logic*, like conditionals and loops. Svelte does.
|
||||
|
||||
To conditionally render some markup, we wrap it in an `if` block:
|
||||
|
||||
```html
|
||||
{#if user.loggedIn}
|
||||
<button on:click={toggle}>
|
||||
Log out
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
{#if !user.loggedIn}
|
||||
<button on:click={toggle}>
|
||||
Log in
|
||||
</button>
|
||||
{/if}
|
||||
```
|
||||
|
||||
Try it — update the component, and click on the buttons.
|
@ -0,0 +1,19 @@
|
||||
<script>
|
||||
let user = { loggedIn: false };
|
||||
|
||||
function toggle() {
|
||||
user.loggedIn = !user.loggedIn;
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if user.loggedIn}
|
||||
<button on:click={toggle}>
|
||||
Log out
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
{#if !user.loggedIn}
|
||||
<button on:click={toggle}>
|
||||
Log in
|
||||
</button>
|
||||
{/if}
|
@ -0,0 +1,17 @@
|
||||
<script>
|
||||
let user = { loggedIn: false };
|
||||
|
||||
function toggle() {
|
||||
user.loggedIn = !user.loggedIn;
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if user.loggedIn}
|
||||
<button on:click={toggle}>
|
||||
Log out
|
||||
</button>
|
||||
{:else}
|
||||
<button on:click={toggle}>
|
||||
Log in
|
||||
</button>
|
||||
{/if}
|
@ -0,0 +1,19 @@
|
||||
---
|
||||
title: Else blocks
|
||||
---
|
||||
|
||||
Since the two conditions — `if user.loggedIn` and `if !user.loggedIn` — are mutually exclusive, we can simplify this component slightly by using an `else` block:
|
||||
|
||||
```html
|
||||
{#if user.loggedIn}
|
||||
<button on:click={toggle}>
|
||||
Log out
|
||||
</button>
|
||||
{:else}
|
||||
<button on:click={toggle}>
|
||||
Log in
|
||||
</button>
|
||||
{/if}
|
||||
```
|
||||
|
||||
> A `#` character always indicates a *block opening* tag. A `/` character always indicates a *block closing* tag. A `:` character, as in `{:else}`, indicates a *block continuation* tag. Don't worry — you've already learned almost all the syntax Svelte adds to HTML.
|
@ -0,0 +1,13 @@
|
||||
<script>
|
||||
let x = 7;
|
||||
</script>
|
||||
|
||||
{#if x > 10}
|
||||
<p>{x} is greater than 10</p>
|
||||
{:else}
|
||||
{#if 5 > x}
|
||||
<p>{x} is less than 5</p>
|
||||
{:else}
|
||||
<p>{x} is between 5 and 10</p>
|
||||
{/if}
|
||||
{/if}
|
@ -0,0 +1,11 @@
|
||||
<script>
|
||||
let x = 7;
|
||||
</script>
|
||||
|
||||
{#if x > 10}
|
||||
<p>{x} is greater than 10</p>
|
||||
{:else if 5 > x}
|
||||
<p>{x} is less than 5</p>
|
||||
{:else}
|
||||
<p>{x} is between 5 and 10</p>
|
||||
{/if}
|
@ -0,0 +1,15 @@
|
||||
---
|
||||
title: Else-if blocks
|
||||
---
|
||||
|
||||
Multiple conditions can be 'chained' together with `else if`:
|
||||
|
||||
```html
|
||||
{#if x > 10}
|
||||
<p>{x} is greater than 10</p>
|
||||
{:else if 5 > x}
|
||||
<p>{x} is less than 5</p>
|
||||
{:else}
|
||||
<p>{x} is between 5 and 10</p>
|
||||
{/if}
|
||||
```
|
@ -0,0 +1,17 @@
|
||||
<script>
|
||||
let cats = [
|
||||
{ id: 'J---aiyznGQ', name: 'Keyboard Cat' },
|
||||
{ id: 'z_AbfPXTKms', name: 'Maru' },
|
||||
{ id: 'OUtn3pvWmpg', name: 'Henri The Existential Cat' }
|
||||
];
|
||||
</script>
|
||||
|
||||
<h1>The Famous Cats of YouTube</h1>
|
||||
|
||||
<ul>
|
||||
<!-- open each block -->
|
||||
<li><a target="_blank" href="https://www.youtube.com/watch?v={cat.id}">
|
||||
{cat.name}
|
||||
</a></li>
|
||||
<!-- close each block -->
|
||||
</ul>
|
@ -0,0 +1,17 @@
|
||||
<script>
|
||||
let cats = [
|
||||
{ id: 'J---aiyznGQ', name: 'Keyboard Cat' },
|
||||
{ id: 'z_AbfPXTKms', name: 'Maru' },
|
||||
{ id: 'OUtn3pvWmpg', name: 'Henri The Existential Cat' }
|
||||
];
|
||||
</script>
|
||||
|
||||
<h1>The Famous Cats of YouTube</h1>
|
||||
|
||||
<ul>
|
||||
{#each cats as { id, name }, i}
|
||||
<li><a target="_blank" href="https://www.youtube.com/watch?v={id}">
|
||||
{i + 1}: {name}
|
||||
</a></li>
|
||||
{/each}
|
||||
</ul>
|
@ -0,0 +1,29 @@
|
||||
---
|
||||
title: Each blocks
|
||||
---
|
||||
|
||||
If you need to loop over lists of data, use an `each` block:
|
||||
|
||||
```html
|
||||
<ul>
|
||||
{#each cats as cat}
|
||||
<li><a target="_blank" href="https://www.youtube.com/watch?v={cat.id}">
|
||||
{cat.name}
|
||||
</a></li>
|
||||
{/each}
|
||||
</ul>
|
||||
```
|
||||
|
||||
> The expression (`cats`, in this case) can be any array or array-like object (i.e. it has a `length` property). You can loop over generic iterables with `each [...iterable]`.
|
||||
|
||||
You can get the current *index* as a second argument, like so:
|
||||
|
||||
```html
|
||||
{#each cats as cat, i}
|
||||
<li><a target="_blank" href="https://www.youtube.com/watch?v={cat.id}">
|
||||
{i + 1}: {cat.name}
|
||||
</a></li>
|
||||
{/each}
|
||||
```
|
||||
|
||||
If you prefer, you can use destructuring — `each cats as { id, name }` — and replace `cat.id` and `cat.name` with `id` and `name`.
|
@ -0,0 +1,23 @@
|
||||
<script>
|
||||
import Thing from './Thing.svelte';
|
||||
|
||||
let things = [
|
||||
{ id: 1, value: 'a' },
|
||||
{ id: 2, value: 'b' },
|
||||
{ id: 3, value: 'c' },
|
||||
{ id: 4, value: 'd' },
|
||||
{ id: 5, value: 'e' }
|
||||
];
|
||||
|
||||
function handleClick() {
|
||||
things = things.slice(1);
|
||||
}
|
||||
</script>
|
||||
|
||||
<button on:click={handleClick}>
|
||||
Remove first thing
|
||||
</button>
|
||||
|
||||
{#each things as thing}
|
||||
<Thing value={thing.value}/>
|
||||
{/each}
|
@ -0,0 +1,9 @@
|
||||
<script>
|
||||
// `value` is updated whenever the prop value changes...
|
||||
export let value;
|
||||
|
||||
// ...but `valueAtStart` is fixed upon initialisation
|
||||
const valueAtStart = value;
|
||||
</script>
|
||||
|
||||
<p>{valueAtStart} / {value}</p>
|
@ -0,0 +1,23 @@
|
||||
<script>
|
||||
import Thing from './Thing.svelte';
|
||||
|
||||
let things = [
|
||||
{ id: 1, value: 'a' },
|
||||
{ id: 2, value: 'b' },
|
||||
{ id: 3, value: 'c' },
|
||||
{ id: 4, value: 'd' },
|
||||
{ id: 5, value: 'e' }
|
||||
];
|
||||
|
||||
function handleClick() {
|
||||
things = things.slice(1);
|
||||
}
|
||||
</script>
|
||||
|
||||
<button on:click={handleClick}>
|
||||
Remove first thing
|
||||
</button>
|
||||
|
||||
{#each things as thing (thing.id)}
|
||||
<Thing value={thing.value}/>
|
||||
{/each}
|
@ -0,0 +1,9 @@
|
||||
<script>
|
||||
// `value` is updated whenever the prop value changes...
|
||||
export let value;
|
||||
|
||||
// ...but `valueAtStart` is fixed upon initialisation
|
||||
const valueAtStart = value;
|
||||
</script>
|
||||
|
||||
<p>{valueAtStart} / {value}</p>
|
@ -0,0 +1,17 @@
|
||||
---
|
||||
title: Keyed each blocks
|
||||
---
|
||||
|
||||
By default, when you modify the value of an `each` block, it will add and remove items at the *end* of the block, and update any values that have changed. That might not be what you want.
|
||||
|
||||
It's easier to show why than to explain. Click the 'Remove first item' button a few times, and notice that it's removing `<Thing>` components from the end and updating the `value` for those that remain. Instead, we'd like to remove the first `<Thing>` component and leave the rest unaffected.
|
||||
|
||||
To do that, we specify a unique identifier for the `each` block:
|
||||
|
||||
```html
|
||||
{#each things as thing (thing.id)}
|
||||
<Thing value={thing.value}/>
|
||||
{/each}
|
||||
```
|
||||
|
||||
The `(thing.id)` tells Svelte how to figure out what changed.
|
@ -0,0 +1,25 @@
|
||||
<script>
|
||||
let promise = getRandomNumber();
|
||||
|
||||
async function getRandomNumber() {
|
||||
const res = await fetch(`tutorial/random-number`);
|
||||
const text = await res.text();
|
||||
|
||||
if (res.ok) {
|
||||
return text;
|
||||
} else {
|
||||
throw new Error(text);
|
||||
}
|
||||
}
|
||||
|
||||
function handleClick() {
|
||||
promise = getRandomNumber();
|
||||
}
|
||||
</script>
|
||||
|
||||
<button on:click={handleClick}>
|
||||
generate random number
|
||||
</button>
|
||||
|
||||
<!-- replace this element -->
|
||||
<p>{promise}</p>
|
@ -0,0 +1,30 @@
|
||||
<script>
|
||||
let promise = getRandomNumber();
|
||||
|
||||
async function getRandomNumber() {
|
||||
const res = await fetch(`tutorial/random-number`);
|
||||
const text = await res.text();
|
||||
|
||||
if (res.ok) {
|
||||
return text;
|
||||
} else {
|
||||
throw new Error(text);
|
||||
}
|
||||
}
|
||||
|
||||
function handleClick() {
|
||||
promise = getRandomNumber();
|
||||
}
|
||||
</script>
|
||||
|
||||
<button on:click={handleClick}>
|
||||
generate random number
|
||||
</button>
|
||||
|
||||
{#await promise}
|
||||
<p>...waiting</p>
|
||||
{:then number}
|
||||
<p>The number is {number}</p>
|
||||
{:catch error}
|
||||
<p style="color: red">{error.message}</p>
|
||||
{/await}
|
@ -0,0 +1,25 @@
|
||||
---
|
||||
title: Await blocks
|
||||
---
|
||||
|
||||
Most web applications have to deal with asynchronous data at some point. Svelte makes it easy to *await* the value of [promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises) directly in your markup:
|
||||
|
||||
```html
|
||||
{#await promise}
|
||||
<p>...waiting</p>
|
||||
{:then number}
|
||||
<p>The number is {number}</p>
|
||||
{:catch error}
|
||||
<p style="color: red">{error.message}</p>
|
||||
{/await}
|
||||
```
|
||||
|
||||
> Only the most recent `promise` is considered, meaning you don't need to worry about race conditions.
|
||||
|
||||
If you know that your promise can't reject, you can omit the `catch` block. You can also omit the first block if you don't want to show anything until the promise resolves:
|
||||
|
||||
```html
|
||||
{#await promise then value}
|
||||
<p>the value is {value}</p>
|
||||
{/await}
|
||||
```
|
@ -0,0 +1,3 @@
|
||||
{
|
||||
"title": "Logic"
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
<script>
|
||||
let m = { x: 0, y: 0 };
|
||||
|
||||
function handleMousemove(event) {
|
||||
m.x = event.clientX;
|
||||
m.y = event.clientY;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
div { width: 100%; height: 100%; }
|
||||
</style>
|
||||
|
||||
<div>
|
||||
The mouse position is {m.x} x {m.y}
|
||||
</div>
|
@ -0,0 +1,16 @@
|
||||
<script>
|
||||
let m = { x: 0, y: 0 };
|
||||
|
||||
function handleMousemove(event) {
|
||||
m.x = event.clientX;
|
||||
m.y = event.clientY;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
div { width: 100%; height: 100%; }
|
||||
</style>
|
||||
|
||||
<div on:mousemove={handleMousemove}>
|
||||
The mouse position is {m.x} x {m.y}
|
||||
</div>
|
@ -0,0 +1,11 @@
|
||||
---
|
||||
title: DOM events
|
||||
---
|
||||
|
||||
As we've briefly seen already, you can listen to any event on an element with the `on:` directive:
|
||||
|
||||
```html
|
||||
<div on:mousemove={handleMousemove}>
|
||||
The mouse position is {m.x} x {m.y}
|
||||
</div>
|
||||
```
|
@ -0,0 +1,16 @@
|
||||
<script>
|
||||
let m = { x: 0, y: 0 };
|
||||
|
||||
function handleMousemove(event) {
|
||||
m.x = event.clientX;
|
||||
m.y = event.clientY;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
div { width: 100%; height: 100%; }
|
||||
</style>
|
||||
|
||||
<div on:mousemove={handleMousemove}>
|
||||
The mouse position is {m.x} x {m.y}
|
||||
</div>
|
@ -0,0 +1,11 @@
|
||||
<script>
|
||||
let m = { x: 0, y: 0 };
|
||||
</script>
|
||||
|
||||
<style>
|
||||
div { width: 100%; height: 100%; }
|
||||
</style>
|
||||
|
||||
<div on:mousemove="{e => m = { x: e.clientX, y: e.clientY }}">
|
||||
The mouse position is {m.x} x {m.y}
|
||||
</div>
|
@ -0,0 +1,15 @@
|
||||
---
|
||||
title: Inline handlers
|
||||
---
|
||||
|
||||
You can also declare event handlers inline:
|
||||
|
||||
```html
|
||||
<div on:mousemove="{e => m = { x: e.clientX, y: e.clientY }}">
|
||||
The mouse position is {m.x} x {m.y}
|
||||
</div>
|
||||
```
|
||||
|
||||
The quote marks are optional, but they're helpful for syntax highlighting in some environments.
|
||||
|
||||
> In some frameworks you may see recommendations to avoid inline event handlers for performance reasons, particularly inside loops. That advice doesn't apply to Svelte — the compiler will always do the right thing, whichever form you choose.
|
@ -0,0 +1,9 @@
|
||||
<script>
|
||||
function handleClick() {
|
||||
alert('clicked')
|
||||
}
|
||||
</script>
|
||||
|
||||
<button on:click={handleClick}>
|
||||
Click me
|
||||
</button>
|
@ -0,0 +1,9 @@
|
||||
<script>
|
||||
function handleClick() {
|
||||
alert('no more alerts')
|
||||
}
|
||||
</script>
|
||||
|
||||
<button on:click|once={handleClick}>
|
||||
Click me
|
||||
</button>
|
@ -0,0 +1,27 @@
|
||||
---
|
||||
title: Event modifiers
|
||||
---
|
||||
|
||||
DOM event handlers can have *modifiers* that alter their behaviour. For example, a handler with a `once` modifier will only run a single time:
|
||||
|
||||
```html
|
||||
<script>
|
||||
function handleClick() {
|
||||
alert('no more alerts')
|
||||
}
|
||||
</script>
|
||||
|
||||
<button on:click|once={handleClick}>
|
||||
Click me
|
||||
</button>
|
||||
```
|
||||
|
||||
The full list of modifiers:
|
||||
|
||||
* `preventDefault` — calls `event.preventDefault()` before running the handler. Useful for e.g. client-side form handling
|
||||
* `stopPropagation` — calls `event.stopPropagation()`, preventing the event reaching the next element
|
||||
* `passive` — improves scrolling performance on touch/wheel events (Svelte will add it automatically where it's safe to do so)
|
||||
* `capture` — fires the handler during the *capture* phase instead of the *bubbling* phase
|
||||
* `once` — remove the handler after the first time it runs
|
||||
|
||||
You can chain modifiers together, e.g. `on:click|once|capture={...}`.
|
@ -0,0 +1,9 @@
|
||||
<script>
|
||||
import Inner from './Inner.svelte';
|
||||
|
||||
function handleMessage(event) {
|
||||
alert(event.detail.text);
|
||||
}
|
||||
</script>
|
||||
|
||||
<Inner on:message={handleMessage}/>
|
@ -0,0 +1,11 @@
|
||||
<script>
|
||||
// setup code goes here
|
||||
|
||||
function sayHello() {
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<button on:click={sayHello}>
|
||||
Click to say hello
|
||||
</button>
|
@ -0,0 +1,9 @@
|
||||
<script>
|
||||
import Inner from './Inner.svelte';
|
||||
|
||||
function handleMessage(event) {
|
||||
alert(event.detail.text);
|
||||
}
|
||||
</script>
|
||||
|
||||
<Inner on:message={handleMessage}/>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue