Add a tutorial (#2132)

pull/2195/head
Rich Harris 6 years ago committed by GitHub
parent bcedaea620
commit 4541d58798
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -48,7 +48,7 @@ What happens if we use the new model as a starting point?
## Introducing Sapper
<aside><p>The <a href="https://sapper.svelte.technology/guide#why-the-name-">name comes from</a> the term for combat engineers, and is also short for Svelte app maker</p></aside>
<aside><p>The <a href="https://sapper.svelte.technology/docs#why-the-name-">name comes from</a> the term for combat engineers, and is also short for Svelte app maker</p></aside>
[Sapper](https://sapper.svelte.technology) is the answer to that question. **Sapper is a Next.js-style framework that aims to meet the eleven criteria at the top of this article while dramatically reducing the amount of code that gets sent to the browser.** It's implemented as Express-compatible middleware, meaning it's easy to understand and customise.
@ -67,7 +67,7 @@ But size is only part of the story. Svelte apps are also extremely performant an
The biggest drawback for many developers evaluating Sapper would be 'but I like React, and I already know how to use it', which is fair.
If you're in that camp, I'd invite you to at least try alternative frameworks. You might be pleasantly surprised! The [Sapper RealWorld](https://github.com/sveltejs/realworld) implementation totals 1,201 lines of source code, compared to 2,377 for the reference implementation, because you're able to express concepts very concisely using Svelte's template syntax (which [takes all of five minutes to master](https://svelte.technology/guide#template-syntax)). You get [scoped CSS](the-zen-of-just-writing-css), with unused style removal and minification built-in, and you can use preprocessors like LESS if you want. You no longer need to use Babel. SSR is ridiculously fast, because it's just string concatenation. And we recently introduced [svelte/store](https://svelte.technology/guide#state-management), a tiny global store that synchronises state across your component hierarchy with zero boilerplate. The worst that can happen is that you'll end up feeling vindicated!
If you're in that camp, I'd invite you to at least try alternative frameworks. You might be pleasantly surprised! The [Sapper RealWorld](https://github.com/sveltejs/realworld) implementation totals 1,201 lines of source code, compared to 2,377 for the reference implementation, because you're able to express concepts very concisely using Svelte's template syntax (which [takes all of five minutes to master](https://svelte.technology/docs#template-syntax)). You get [scoped CSS](the-zen-of-just-writing-css), with unused style removal and minification built-in, and you can use preprocessors like LESS if you want. You no longer need to use Babel. SSR is ridiculously fast, because it's just string concatenation. And we recently introduced [svelte/store](https://svelte.technology/docs#state-management), a tiny global store that synchronises state across your component hierarchy with zero boilerplate. The worst that can happen is that you'll end up feeling vindicated!
But there are trade-offs nonetheless. Some people have a pathological aversion to any form of 'template language', and maybe that applies to you. JSX proponents will clobber you with the 'it's just JavaScript' mantra, and therein lies React's greatest strength, which is that it is infinitely flexible. That flexibility comes with its own set of trade-offs, but we're not here to discuss those.

@ -86,7 +86,7 @@ If you need to support IE11 and friends, you will need to use a transpiler like
## New lifecycle hooks
In addition to `oncreate` and `ondestroy`, Svelte v2 adds two more [lifecycle hooks](guide#lifecycle-hooks) for responding to state changes:
In addition to `oncreate` and `ondestroy`, Svelte v2 adds two more [lifecycle hooks](docs#lifecycle-hooks) for responding to state changes:
```js
export default {
@ -169,7 +169,7 @@ This change might seem annoying initially, but it's the right move: among other
## event_handler.destroy
If your app has [custom event handlers](guide#custom-event-handlers), they must return an object with a `destroy` method, *not* a `teardown` method (this aligns event handlers with the component API).
If your app has [custom event handlers](docs#custom-event-handlers), they must return an object with a `destroy` method, *not* a `teardown` method (this aligns event handlers with the component API).
## No more type coercion

@ -9,9 +9,6 @@
let width = 500;
let height = 200;
let barWidth;
let xScale;
let yScale;
function formatMobile(tick) {
return "'" + tick % 100;
@ -25,10 +22,8 @@
.domain([0, Math.max.apply(null, yTicks)])
.range([height - padding.bottom, padding.top]);
$: {
const innerWidth = width - (padding.left + padding.right);
barWidth = innerWidth / xTicks.length;
}
$: innerWidth = width - (padding.left + padding.right);
$: barWidth = innerWidth / xTicks.length;
</script>
<style>

@ -2,8 +2,7 @@
let paused = true;
let t = 0;
let d;
let icon, bg;
let bg;
$: icon = `https://icon.now.sh/${paused ? 'play' : 'pause'}_circle_filled`;

@ -53,7 +53,7 @@
<main>
{#if item}
<Item {item}/>
{:elseif page}
{:else if page}
<List {page}/>
{/if}
</main>

@ -11,13 +11,6 @@
let width = 500;
let height = 200;
let xScale;
let yScale;
let minX;
let maxX;
let path;
let area;
$: xScale = scaleLinear()
.domain([minX, maxX])
.range([padding.left, width - padding.right]);

@ -4,31 +4,30 @@
export let points;
let svg;
let width = 500;
let height = 200;
const padding = { top: 20, right: 40, bottom: 40, left: 25 };
let xScale;
$: xScale = scaleLinear()
.domain([0, 20])
.range([padding.left, width - padding.right]);
let yScale;
$: yScale = scaleLinear()
.domain([0, 12])
.range([height - padding.bottom, padding.top]);
let width = 500;
let height = 200;
let xTicks;
$: xTicks = width > 180 ?
[0, 4, 8, 12, 16, 20] :
[0, 10, 20];
let yTicks;
$: yTicks = height > 180 ?
[0, 2, 4, 6, 8, 10, 12] :
[0, 4, 8, 12];
onMount(resize);
let svg;
function resize() {
({ width, height } = svg.getBoundingClientRect());
}

@ -2,15 +2,12 @@
import { onMount } from 'svelte';
let time = new Date();
let hours, minutes, seconds;
$: {
// this block runs reactively, whenever
// `time` changes
hours = time.getHours();
minutes = time.getMinutes();
seconds = time.getSeconds();
}
// these automatically update when `time`
// changes, because of the `$:` prefix
$: hours = time.getHours();
$: minutes = time.getMinutes();
$: seconds = time.getSeconds();
onMount(() => {
const interval = setInterval(() => {

@ -87,13 +87,13 @@ You can combine the two blocks above with `{:else}`:
{/if}
```
You can also use `{:elseif ...}`:
You can also use `{:else if ...}`:
```html
<!--{ title: 'If, else and elseif' }-->
<!--{ title: 'If, else and else if' }-->
{#if x > 10}
<p>{x} is greater than 10</p>
{:elseif 5 > x}
{:else if 5 > x}
<p>{x} is less than 5</p>
{:else}
<p>{x} is between 5 and 10</p>
@ -176,7 +176,7 @@ You can access the index of the current element with *expression* as *name*, *in
}
```
> By default, if the list `a, b, c` becomes `a, c`, Svelte will *remove* the third block and *change* the second from `b` to `c`, rather than removing `b`. If that's not what you want, use a [keyed each block](guide#keyed-each-blocks).
> By default, if the list `a, b, c` becomes `a, c`, Svelte will *remove* the third block and *change* the second from `b` to `c`, rather than removing `b`. If that's not what you want, use a [keyed each block](docs#keyed-each-blocks).
You can use destructuring patterns on the elements of the array:
@ -237,7 +237,7 @@ If the expression in `{#await expression}` *isn't* a promise, Svelte skips ahead
### Directives
Directives allow you to add special instructions for adding [event handlers](guide#event-handlers), [bindings](guide#bindings), [transitions](guide#transitions) and so on. We'll cover each of those in later stages of this guide for now, all you need to know is that directives can be identified by the `:` character:
Directives allow you to add special instructions for adding [event handlers](docs#event-handlers), [bindings](docs#bindings), [transitions](docs#transitions) and so on. We'll cover each of those in later stages of this guide for now, all you need to know is that directives can be identified by the `:` character:
```html
<!--{ title: 'Element directives' }-->

@ -30,7 +30,7 @@ Open the example above in the REPL and inspect the element to see what has happe
This is vastly simpler than achieving the same effect via [Shadow DOM](http://caniuse.com/#search=shadow%20dom) and works everywhere without polyfills.
> Svelte will add a `<style>` tag to the page containing your scoped styles. Dynamically adding styles may be impossible if your site has a [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP). If that's the case, you can use scoped styles by [server-rendering your CSS](guide#rendering-css) and using the `css: false` compiler option (or `--no-css` with the CLI).
> Svelte will add a `<style>` tag to the page containing your scoped styles. Dynamically adding styles may be impossible if your site has a [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP). If that's the case, you can use scoped styles by [server-rendering your CSS](docs#rendering-css) and using the `css: false` compiler option (or `--no-css` with the CLI).
### Cascading rules
@ -95,7 +95,7 @@ For rules *not* to be removed, they must apply to the component's markup. As far
</script>
```
Instead of manually manipulating the DOM, you should always use the `class` attribute (or the [class directive](https://svelte.technology/guide#classes)):
Instead of manually manipulating the DOM, you should always use the `class` attribute (or the [class directive](https://svelte.technology/docs#classes)):
```html
<!-- { repl: false } -->

@ -51,7 +51,7 @@ Often, the name of the property will be the same as the value, in which case we
<Widget {foo}/>
```
> Note that props are *one-way* — to get data from a child component into a parent component, use [bindings](guide#bindings).
> Note that props are *one-way* — to get data from a child component into a parent component, use [bindings](docs#bindings).
### Composing with `<slot>`

@ -132,4 +132,4 @@ You can do that with the `<svelte:head>` tag:
</svelte:head>
```
When [server rendering](guide#server-side-rendering), the `<head>` contents can be extracted separately to the rest of the markup.
When [server rendering](docs#server-side-rendering), the `<head>` contents can be extracted separately to the rest of the markup.

@ -95,7 +95,7 @@ The example above can be achieved with modifiers — no need for a separate even
### Component events
Events are an excellent way for [nested components](guide#nested-components) to communicate with their parents. Let's revisit our earlier example, but turn it into a `<CategoryChooser>` component:
Events are an excellent way for [nested components](docs#nested-components) to communicate with their parents. Let's revisit our earlier example, but turn it into a `<CategoryChooser>` component:
```html
<!-- { filename: 'CategoryChooser.html', repl: false } -->

@ -5,7 +5,7 @@ title: Bindings
### Bindings
As we've seen, data can be passed down to elements and components with attributes and [props](guide#props). Occasionally, you need to get data back *up*; for that we use bindings.
As we've seen, data can be passed down to elements and components with attributes and [props](docs#props). Occasionally, you need to get data back *up*; for that we use bindings.
#### Component bindings

@ -51,7 +51,7 @@ const props = { answer: 42 };
const { html, css, head } = Thing.render(props);
```
[Lifecycle hooks](guide#lifecycle-hooks) will *not* run, with the exception of `onDestroy`, because the component is never 'mounted'.
[Lifecycle hooks](docs#lifecycle-hooks) will *not* run, with the exception of `onDestroy`, because the component is never 'mounted'.
> The SSR compiler will generate a CommonJS module for each of your components meaning that `import` and `export` statements are converted into their `require` and `module.exports` equivalents. If your components have non-component dependencies, they must also work as CommonJS modules in Node. If you're using ES2015 modules, we recommend the [`esm`](https://github.com/standard-things/esm) module for automatically converting them to CommonJS.
@ -59,7 +59,7 @@ const { html, css, head } = Thing.render(props);
#### Rendering styles
You can also extract any [scoped styles](guide#scoped-styles) that are used by the component or its children:
You can also extract any [scoped styles](docs#scoped-styles) that are used by the component or its children:
```js
const { css } = Thing.render(data);
@ -71,7 +71,7 @@ You could put the resulting `css` in a separate stylesheet, or include them in t
#### Rendering `<head>` contents
If your component, any of its children, use the `<svelte:head>` [component](guide#-head-tags), you can extract the contents:
If your component, any of its children, use the `<svelte:head>` [component](docs#-head-tags), you can extract the contents:
```js
const { head } = Thing.render(data);

@ -39,7 +39,7 @@ store.set({ name: 'everybody' }); // 'hello everybody'
### Creating components with stores
Let's adapt our [very first example](guide#understanding-svelte-components):
Let's adapt our [very first example](docs#understanding-svelte-components):
```html
<!-- { repl: false } -->
@ -169,7 +169,7 @@ Each component gets a reference to `this.store`. This allows you to attach behav
const listener = this.store.on('state', ({ current }) => {
// ...
});
// listeners are not automatically removed — cancel
// them to prevent memory leaks
this.on('destroy', listener.cancel);

@ -49,15 +49,15 @@ It's easier to show the effect of this than to describe it. Open the following e
import { slide } from 'svelte/transition';
const names = ['Alice', 'Barry', 'Cecilia', 'Douglas', 'Eleanor', 'Felix', 'Grace', 'Horatio', 'Isabelle'];
function random() {
return names
.filter(() => Math.random() < 0.5)
.map(name => ({ name }));
}
let people = random();
function update() {
people = random();
}
@ -67,7 +67,7 @@ It's easier to show the effect of this than to describe it. Open the following e
### Hydration
If you're using [server-side rendering](guide#server-side-rendering), it's likely that you'll need to create a client-side version of your app *on top of* the server-rendered version. A naive way to do that would involve removing all the existing DOM and rendering the client-side app in its place:
If you're using [server-side rendering](docs#server-side-rendering), it's likely that you'll need to create a client-side version of your app *on top of* the server-rendered version. A naive way to do that would involve removing all the existing DOM and rendering the client-side app in its place:
```js
import App from './App.html';

@ -45,7 +45,7 @@ One crucial difference is that styles are *fully encapsulated* — whereas Svelt
### Using `<slot>`
Custom elements can use [slots](guide#composing-with-slot) to place child elements, just like regular Svelte components.
Custom elements can use [slots](docs#composing-with-slot) to place child elements, just like regular Svelte components.
### Firing events

@ -68,7 +68,7 @@ const { questions, answer } = component.get();
console.log(answer); // 'ask your mother'
```
This will also retrieve the value of [computed properties](guide#computed-properties).
This will also retrieve the value of [computed properties](docs#computed-properties).
> Previous versions of Svelte allowed you to specify a key to retrieve a specific value — this was removed in version 2.
@ -85,7 +85,7 @@ const listener = component.on('thingHappened', event => {
listener.cancel();
```
Each component has three built-in events, corresponding to their [lifecycle hooks](guide#lifecycle-hooks):
Each component has three built-in events, corresponding to their [lifecycle hooks](docs#lifecycle-hooks):
```js
component.on('state', ({ changed, current, previous }) => {
@ -112,7 +112,7 @@ component.fire('thingHappened', {
});
```
At first glance `component.on(...)` and `component.fire(...)` aren't particularly useful, but it'll become more so when we learn about [nested components](guide#nested-components) and [component events](guide#component-events).
At first glance `component.on(...)` and `component.fire(...)` aren't particularly useful, but it'll become more so when we learn about [nested components](docs#nested-components) and [component events](docs#component-events).
### component.destroy()
@ -150,6 +150,6 @@ This gives you access to standard options like `target` and `data`, but can also
### component.root
In [nested components](guide#nested-components), each component has a `root` property pointing to the top-level root component that is, the one instantiated with `new MyComponent({...})`.
In [nested components](docs#nested-components), each component has a `root` property pointing to the top-level root component that is, the one instantiated with `new MyComponent({...})`.
> Earlier versions of Svelte had a `component.observe(...)` method. This was removed in version 2, in favour of the `onstate` [lifecycle hook](guide#lifecycle-hooks), but is still available via [svelte-extras](https://github.com/sveltejs/svelte-extras).
> Earlier versions of Svelte had a `component.observe(...)` method. This was removed in version 2, in favour of the `onstate` [lifecycle hook](docs#lifecycle-hooks), but is still available via [svelte-extras](https://github.com/sveltejs/svelte-extras).

@ -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,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: &lt;img&gt; 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,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,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,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,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,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…
Cancel
Save