Einstieg in
enterJS Svelte Day 2022 | Nils Röhrig | REWE digital
- Was ist Svelte?
- Wo kommt Svelte her?
- Was kann Svelte?
- Was macht Svelte anders?
- Wie sieht das Zusammenspiel aus?
Was ist Svelte?
Kerneigenschaften
- Komponenten-Framework
- Kapselung von UI-Elementen
- Deklarative Komposition
Beispiel-Komponente
<form action="/login" method="post">
<Input type="email" label="E-Mail" name="email" />
<Input type="password" label="Password" name="password" />
<Button type="submit" label="Log in" />
</form>
Wo kommt Svelte her?
HTML & CSS
.block__element--modifier
<form action="/login" method="post" class="form form--login">
<div class="form__row">
<label for="email">E-Mail</label>
<input type="email" name="email" class="form__input form__input--email" />
</div>
<div class="form__row">
<label for="password">Password</label>
<input type="password" class="form__input form__input--password" />
</div>
<div class="form__row">
<button class="form__button form__button--submit">Submit</button>
</div>
</form>
.form {}
.form--login {}
.form__row {}
.form__input {}
.form__input--email {}
.form__input--password {}
.form__button {}
.form__button--submit {}
Bootstrap
<div class="modal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title">Modal Example</h1>
<button class="btn-close" data-bs-dismiss="modal" />
</div>
<div class="modal-body">
<p>Example content.</p>
</div>
<div class="modal-footer">
<button class="btn btn-primary" data-bs-dismiss="modal">Ok</button>
</div>
</div>
</div>
</div>
JavaScript
Model-View-ViewModel
React
- Veröffentlicht 2013 von Facebook
- Game Changer
- Kein MVVM
- Immutability
- One-Way-Dataflow
- Virtuelles DOM
One-Way-Dataflow
Virtual DOM
Und was hat das mit Svelte zu tun?
- Veröffentlicht 2016 von Rich Harris
- Beeinflusst durch Vorgänger
- Übernahme guter Aspekte
- Überarbeitung weniger guter Aspekte
- Zunächst Nischendasein
Was kann Svelte?
Svelte ist ein Compiler
Svelte-Komponente
<h1>Hello Svelte Day!</h1>
Svelte-Komponente
JavaScript
Compiler
<h1>Hello Svelte Day!</h1>
/* HelloSvelteDay.svelte generated by Svelte v3.46.4 */
import { SvelteComponent, detach, element, init,
insert, noop, safe_not_equal } from "svelte/internal";
function create_fragment(ctx) {
let h1;
return {
c() {
h1 = element("h1");
h1.textContent = "Hello Svelte Day!";
},
m(target, anchor) {
insert(target, h1, anchor);
},
p: noop,
i: noop,
o: noop,
d(detaching) {
if (detaching) detach(h1);
},
};
}
class HelloSvelteDay extends SvelteComponent {
constructor(options) {
super();
init(this, options, null, create_fragment, safe_not_equal, {});
}
}
export default HelloSvelteDay;
JavaScript
<h1>Hello Svelte Day!</h1>
/* HelloSvelteDay.svelte generated by Svelte v3.46.4 */
import { SvelteComponent, detach, element, init,
insert, noop, safe_not_equal } from "svelte/internal";
function create_fragment(ctx) {
let h1;
return {
c() {
h1 = element("h1");
h1.textContent = "Hello Svelte Day!";
},
m(target, anchor) {
insert(target, h1, anchor);
},
p: noop,
i: noop,
o: noop,
d(detaching) {
if (detaching) detach(h1);
},
};
}
class HelloSvelteDay extends SvelteComponent {
constructor(options) {
super();
init(this, options, null, create_fragment, safe_not_equal, {});
}
}
export default HelloSvelteDay;
Svelte ist eine Sprache
Komponenten-Struktur
<script>
// STATE & BEHAVIOR
</script>
<style>
/* PRESENTATION */
</style>
<!-- DECLARATIVE MARKUP -->
Direktiven
<script>
import { fade } from "svelte/transition";
let value = 0;
</script>
<input bind:value type="number" />
<button on:click={() => console.log(value)}>Log</button>
<p transition:fade>Fading</p>
Kontrollstrukturen
<script>
const fruits = ["banana", "apple", "orange"];
let visible = false;
</script>
<p>
<button on:click={() => (visible = !visible)}>Toggle Fruit Visibility</button>
</p>
<h2>Fruits</h2>
{#if visible}
<ul>
{#each fruits as fruit}
<li>{fruit}</li>
{/each}
</ul>
{:else}
<p>Fruits are invisible, please toggle fruit visibility.</p>
{/if}
Reaktivität
Ausgangstabelle
Manuelles Update
Direktes Reaktives Update
Transitives Reaktives Update
Event Handling
State Management
<script>
let count = 0;
</script>
<p>
<button on:click={() => count++}> increment </button>
<button on:click={() => count--}> decrement </button>
</p>
<p>{count}</p>
Local State
- Einfache Variablen
- Direktzugriff in der Komponente
- Zuweisung triggert Update
- Obacht bei Sets, Arrays, etc.
Props
<!-- PropsContainer -->
<script>
import PropsDisplayer from "./PropsDisplayer.svelte"
</script>
<PropsDisplayer name="Svelte Day" />
<!-- PropsDisplayer -->
<script>
export let name
</script>
<p>Hello {name}!</p>
Props
<!-- PropsContainer -->
<script>
import PropsDisplayer from "./PropsDisplayer.svelte"
</script>
<PropsDisplayer name="Svelte Day" />
<!-- PropsDisplayer -->
<script>
export let name
</script>
<p>Hello {name}!</p>
Props
<!-- PropsContainer -->
<script>
import PropsDisplayer from "./PropsDisplayer.svelte"
</script>
<PropsDisplayer name="Svelte Day" />
<!-- PropsDisplayer -->
<script>
export let name
</script>
<p>Hello {name}!</p>
Props
<!-- PropsContainer -->
<script>
import PropsDisplayer from "./PropsDisplayer.svelte"
</script>
<PropsDisplayer name="Svelte Day" />
<!-- PropsDisplayer -->
<script>
export let name
</script>
<p>Hello {name}!</p>
Context
- Zentraler Speicher im Framework
- Gebunden an Hierarchie
- Datenzugriff nur wo nötig
- Nicht reaktiv
Context-Beispiel
<!-- ContextContainer -->
<script>
import { setContext } from "svelte"
import ContextDisplayer from "./ContextDisplayer.svelte"
setContext("name", "Svelte Day")
</script>
<ContextDisplayer />
<!-- ContextDisplayer -->
<script>
import { getContext } from "svelte"
const name = getContext("name")
</script>
<p>Hello {name}!</p>
Context-Beispiel
<!-- ContextContainer -->
<script>
import { setContext } from "svelte"
import ContextDisplayer from "./ContextDisplayer.svelte"
setContext("name", "Svelte Day")
</script>
<ContextDisplayer />
<!-- ContextDisplayer -->
<script>
import { getContext } from "svelte"
const name = getContext("name")
</script>
<p>Hello {name}!</p>
Context-Beispiel
<!-- ContextContainer -->
<script>
import { setContext } from "svelte"
import ContextDisplayer from "./ContextDisplayer.svelte"
setContext("name", "Svelte Day")
</script>
<ContextDisplayer />
<!-- ContextDisplayer -->
<script>
import { getContext } from "svelte"
const name = getContext("name")
</script>
<p>Hello {name}!</p>
Stores
- Publish/Subscribe-Modell
- Verfügbar in gesamter App
- Besonderer Sprachsupport
- Reaktiv
Counter Store
import { writable } from "svelte/store";
const { update, subscribe } = writable(0);
export default {
subscribe,
increment: () => update((value) => value + 1),
decrement: () => update((value) => value - 1),
};
<script>
import counter from "./counter.js";
</script>
<button on:click={counter.decrement}>decrement</button>
<button on:click={counter.increment}>increment</button>
<p><output>{$counter}</output></p>
Counter Store
import { writable } from "svelte/store";
const { update, subscribe } = writable(0);
export default {
subscribe,
increment: () => update((value) => value + 1),
decrement: () => update((value) => value - 1),
};
<script>
import counter from "./counter.js";
</script>
<button on:click={counter.decrement}>decrement</button>
<button on:click={counter.increment}>increment</button>
<p><output>{$counter}</output></p>
Counter Store
import { writable } from "svelte/store";
const { update, subscribe } = writable(0);
export default {
subscribe,
increment: () => update((value) => value + 1),
decrement: () => update((value) => value - 1),
};
<script>
import counter from "./counter.js";
</script>
<button on:click={counter.decrement}>decrement</button>
<button on:click={counter.increment}>increment</button>
<p><output>{$counter}</output></p>
Stylesheets
Was macht Svelte anders?
- Vermeidung unnötiger Arbeit
- Geringere Code-Menge
- Funktionsvielfalt
- Nähe zur Plattform
- Flache Lernkurve für Web-Entwickler
Verzicht auf Virtual DOM
- Ist-/Soll-Vergleich ist aufwändig
- Virtual DOM ermuntert unnötige Arbeit
- In Svelte unnötig, weil es ein Compiler ist
- Änderungen zur Build-Zeit bekannt
Vermeidung unnötiger Arbeit
React
Svelte
import React, { useState } from "react";
function UnnecessaryWork(props) {
const [fruit, setFruit] = useState(null);
const items = props.items ?? ["banana", "apple", "orange"];
return (
<>
<p>Your favorite Fruit: {fruit ?? "nothing"}</p>
<hr />
<p>Pick yor favorite fruit:</p>
<ul>
{items.map((item) => (
<li>
<button onClick={() => setFruit(item)}>
{item}
</button>
</li>
))}
</ul>
</>
);
}
<script>
export let items = ["banana", "apple", "orange"];
let fruit;
</script>
<p>Your favorite Fruit: {fruit ?? 'nothing'}</p>
<hr />
<p>Pick yor favorite fruit:</p>
<ul>
{#each items as fr}
<li>
<button on:click={() => (fruit = fr)}>
{fr}
</button>
</li>
{/each}
</ul>
Vermeidung unnötiger Arbeit
React
Svelte
import React, { useState } from "react";
function UnnecessaryWork(props) {
const [fruit, setFruit] = useState(null);
const items = props.items ?? ["banana", "apple", "orange"];
return (
<>
<p>Your favorite Fruit: {fruit ?? "nothing"}</p>
<hr />
<p>Pick yor favorite fruit:</p>
<ul>
{items.map((item) => (
<li>
<button onClick={() => setFruit(item)}>
{item}
</button>
</li>
))}
</ul>
</>
);
}
<script>
export let items = ["banana", "apple", "orange"];
let fruit;
</script>
<p>Your favorite Fruit: {fruit ?? 'nothing'}</p>
<hr />
<p>Pick yor favorite fruit:</p>
<ul>
{#each items as fr}
<li>
<button on:click={() => (fruit = fr)}>
{fr}
</button>
</li>
{/each}
</ul>
Vermeidung unnötiger Arbeit
React
Svelte
import React, { useState } from "react";
function UnnecessaryWork(props) {
const [fruit, setFruit] = useState(null);
const items = props.items ?? ["banana", "apple", "orange"];
return (
<>
<p>Your favorite Fruit: {fruit ?? "nothing"}</p>
<hr />
<p>Pick yor favorite fruit:</p>
<ul>
{items.map((item) => (
<li>
<button onClick={() => setFruit(item)}>
{item}
</button>
</li>
))}
</ul>
</>
);
}
<script>
export let items = ["banana", "apple", "orange"];
let fruit;
</script>
<p>Your favorite Fruit: {fruit ?? 'nothing'}</p>
<hr />
<p>Pick yor favorite fruit:</p>
<ul>
{#each items as fr}
<li>
<button on:click={() => (fruit = fr)}>
{fr}
</button>
</li>
{/each}
</ul>
Geringere Code-Menge
React
Svelte
import React, { useState } from "react";
export default function Summing() {
const [operand1, setOperand1] = useState(0);
const [operand2, setOperand2] = useState(0);
return (
<>
<h1>Summing</h1>
<input
type="number"
value={operand1}
onChange={(e) => setOperand1(Number(e.target.value))}
/>
+
<input
type="number"
value={operand2}
onChange={(e) => setOperand2(Number(e.target.value))}
/>
=
<strong>{operand1 + operand2}</strong>
</>
);
}
<script>
let operand1 = 0;
let operand2 = 0;
</script>
<h1>Summing</h1>
<input type="number" bind:value={operand1} />
+
<input type="number" bind:value={operand2} />
=
<strong>{operand1 + operand2}</strong>
Geringere Code-Menge
React
Svelte
import React, { useState } from "react";
export default function Summing() {
const [operand1, setOperand1] = useState(0);
const [operand2, setOperand2] = useState(0);
return (
<>
<h1>Summing</h1>
<input
type="number"
value={operand1}
onChange={(e) => setOperand1(Number(e.target.value))}
/>
+
<input
type="number"
value={operand2}
onChange={(e) => setOperand2(Number(e.target.value))}
/>
=
<strong>{operand1 + operand2}</strong>
</>
);
}
<script>
let operand1 = 0;
let operand2 = 0;
</script>
<h1>Summing</h1>
<input type="number" bind:value={operand1} />
+
<input type="number" bind:value={operand2} />
=
<strong>{operand1 + operand2}</strong>
Geringere Code-Menge
React
Svelte
import React, { useState } from "react";
export default function Summing() {
const [operand1, setOperand1] = useState(0);
const [operand2, setOperand2] = useState(0);
return (
<>
<h1>Summing</h1>
<input
type="number"
value={operand1}
onChange={(e) => setOperand1(Number(e.target.value))}
/>
+
<input
type="number"
value={operand2}
onChange={(e) => setOperand2(Number(e.target.value))}
/>
=
<strong>{operand1 + operand2}</strong>
</>
);
}
<script>
let operand1 = 0;
let operand2 = 0;
</script>
<h1>Summing</h1>
<input type="number" bind:value={operand1} />
+
<input type="number" bind:value={operand2} />
=
<strong>{operand1 + operand2}</strong>
Geringere Code-Menge
React
Svelte
import React, { useState } from "react";
export default function Summing() {
const [operand1, setOperand1] = useState(0);
const [operand2, setOperand2] = useState(0);
return (
<>
<h1>Summing</h1>
<input
type="number"
value={operand1}
onChange={(e) => setOperand1(Number(e.target.value))}
/>
+
<input
type="number"
value={operand2}
onChange={(e) => setOperand2(Number(e.target.value))}
/>
=
<strong>{operand1 + operand2}</strong>
</>
);
}
<script>
let operand1 = 0;
let operand2 = 0;
</script>
<h1>Summing</h1>
<input type="number" bind:value={operand1} />
+
<input type="number" bind:value={operand2} />
=
<strong>{operand1 + operand2}</strong>
Geringere Code-Menge
React
Svelte
import React, { useState } from "react";
export default function Summing() {
const [operand1, setOperand1] = useState(0);
const [operand2, setOperand2] = useState(0);
return (
<>
<h1>Summing</h1>
<input
type="number"
value={operand1}
onChange={(e) => setOperand1(Number(e.target.value))}
/>
+
<input
type="number"
value={operand2}
onChange={(e) => setOperand2(Number(e.target.value))}
/>
=
<strong>{operand1 + operand2}</strong>
</>
);
}
<script>
let operand1 = 0;
let operand2 = 0;
</script>
<h1>Summing</h1>
<input type="number" bind:value={operand1} />
+
<input type="number" bind:value={operand2} />
=
<strong>{operand1 + operand2}</strong>
Funktionsvielfalt
Svelte | React | |
---|---|---|
CSS-Support | ✅ |
❌ |
Transitionen | ✅ | ❌ |
Animationen | ✅ | ❌ |
State Management | ✅ | / |
Deklarativer Zugriff auf Head, Body oder Window | ✅ | ❌ |
Nähe zur Plattform
React
Svelte
import React, { useRef } from "react";
import tippy from "tippy.js";
export default function App() {
const tippyRef = useRef();
if (tippyRef != null) {
tippy(tippyRef.current, {
content: "Tippy Tooltip!",
});
}
return (
<>
<button ref={tippyRef}>Tippy Button</button>
</>
);
}
<script>
import tippy from "tippy.js";
</script>
<button use:tippy={{ content: 'Tippy Tooltip!' }}>
Tippy Button!
</button>
Nähe zur Plattform
React
Svelte
import React, { useRef } from "react";
import tippy from "tippy.js";
export default function App() {
const tippyRef = useRef();
if (tippyRef != null) {
tippy(tippyRef.current, {
content: "Tippy Tooltip!",
});
}
return (
<>
<button ref={tippyRef}>Tippy Button</button>
</>
);
}
<script>
import tippy from "tippy.js";
</script>
<button use:tippy={{ content: 'Tippy Tooltip!' }}>
Tippy Button!
</button>
Nähe zur Plattform
React
Svelte
import React, { useRef } from "react";
import tippy from "tippy.js";
export default function App() {
const tippyRef = useRef();
if (tippyRef != null) {
tippy(tippyRef.current, {
content: "Tippy Tooltip!",
});
}
return (
<>
<button ref={tippyRef}>Tippy Button</button>
</>
);
}
<script>
import tippy from "tippy.js";
</script>
<button use:tippy={{ content: 'Tippy Tooltip!' }}>
Tippy Button!
</button>
Lernkurve
- HTML, CSS, JS sind schon die halbe Miete
- Syntaxerweiterungen ähneln Template-Engines
- Svelte-Spezifika sind leicht unterwegs zu erlernen
Wie sieht das Zusammenspiel aus?
- Offizielles Tutorial bearbeiten
- Im REPL experimentieren
- Svelte für einen kleinen Teil eines Projekts / Produkts einsetzen
Vielen Dank!
Einstieg in Svelte (enterJS Svelte Day 22)
By Nils Röhrig
Einstieg in Svelte (enterJS Svelte Day 22)
- 1,869