Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Learning Svelte

279 views

Published on

this is a beginner talk covering how to build apps in Svelte

Published in: Software
  • Be the first to comment

Learning Svelte

  1. 1. Svelte Chris Noring @chris_noring Cloud Advocate at Microsoft
  2. 2. WHY • Less cruft/ boilerplate • It’s fast, no Virtual DOM • No script tag, Svelte bundles stand-alone components, without including a framework script as a dependency - Svelte is a compiler, it’s only framework during the build process • Svelte can therefore be used to • Swap out individual components • Replace an entire app, up to you • It works with the standard rather than trying to invent it. Svelte looks and feels like just working with HTML, CSS and JS
  3. 3. WHAT • One component per file • Styles scoped by default • It’s just HTML, CSS, JS • Powerful features like expressive directives. Usual suspects like Bindings, ability to • watch changes, • Input props, • Events etc.
  4. 4. Show me code // Hello.svelte <script> let name = 'world'; </script> <h1>Hello {name}</h1> /* App.svelte generated by Svelte v3.16.7 */ 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 ${name}!`; }, m(target, anchor) { insert(target, h1, anchor); }, p: noop, i: noop, o: noop, d(detaching) { if (detaching) detach(h1); } }; } let name = "world"; class App extends SvelteComponent { constructor(options) { super(); init(this, options, null, create_fragment, safe_not_equal, {}); } } export default App;
  5. 5. How to get started • Svelte.dev, great site with interactive tutorial • Create your own project, • npx degit sveltejs/template hello-app • npm install • npm run dev • Install extensions for eitherVim/VS Code
  6. 6. Interpolation, {} <script> let src = 'tutorial/image.gif’; Let title =‘something’; </script> <img src={src}> <div>{title}</div> In an attribute or directly in the markup
  7. 7. A typical component <style> p { color: purple; font-family: 'Comic Sans MS', cursive; font-size: 2em; } </style> <script> let src = 'tutorial/image.gif’; let description = "Rick Astley" </script> <p> <img src={src}> <p> <div>{description}</div> Scoped styles Code section Markup + interpolation
  8. 8. Import & use a component // App.svelte <script> import Nested from './Nested.svelte'; </script> <p>Some text</p> <Nested /> Import Use
  9. 9. Render HTML <script> let string = `this string contains some <strong>HTML!!!</strong>`; </script> <p>{@html string}</p> @html string-containing HTML HTML
  10. 10. Handle Events <script> let count = 0; function handleClick() { // event handler code goes here count += 1; } </script> <button on:click={handleClick}> Clicked {count} {count === 1 ? 'time' : 'times'} </button> Method declaration Bind “handleClick” to “click” event with on:click
  11. 11. Additional event example: mousemove <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> Bind “handleMousemove” to “mousemove” event with on:mousemove Method declarationc
  12. 12. Inline handlers <div on:mousemove={handleMove}> </div> <div on:mousemove={e => m = { x : e.clientX, y: e.clientY }}> </div> Lambda function Reference to function
  13. 13. Event modifiers <script> function handleClick() { console.log('only run once'); } </script> <div on:click|once={handleClick}> I dare you click me more than once </div> • `preventDefault` — calls event.preventDefault() before running the handler. Useful for client-side form handling, for example. • `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 • `self` — only trigger handler if event.target is the element itself Additionally you can chain modifiers like so `on:event|modifier|secondModifier={handler}` Modifier
  14. 14. Computed state <script> let count = 0; function handleClick() { count += 1; } $: doubled = count * 2; </script> <button on:click={handleClick}> Clicked {count} {count === 1 ? 'time' : 'times'} </button> {doubled} Runs when count changes, calculates a new value for double
  15. 15. Reactive statements <script> let count = 0; function handleClick() { count += 1; } $: console.log(`Value changed ${count}`); </script> <button on:click={handleClick}> Clicked {count} {count === 1 ? 'time' : 'times'} </button> Runs when count changes, i.e can also run statements
  16. 16. Grouped reactive statements <script> let count = 0; function handleClick() { count += 1; } $: { console.log(`the count is ${count}`) alert(count) } </script> <button on:click={handleClick}> Clicked {count} {count === 1 ? 'time' : 'times'} </button> Can run several lines of code, using {}
  17. 17. Reactivity and assignments Svelte triggers on assignments. This means that the following code WONT update the UI: array.push(1) but this will: array = [...array, 1]
  18. 18. Component input // Nested.svelte <script> export let answer; </script> <p>The answer is {answer}</p> <script> import Nested from './Nested.svelte'; </script> <div>something...</div> <Nested answer={42} /> export keyword is used to expose answer
  19. 19. Component input – spread props <script> val obj = { firstname: 'ada’, lastname: 'lovelace’, profession: 'programmer' }; </script> <Person firstname={obj.firstname} lastname={obj.lastname} profession={obj.profession} > <Person {...obj} />
  20. 20. Component output // Inner.svelte <script> import { createEventDispatcher } from 'svelte'; let dispatcher = createEventDispatcher(); function handleEvent() { dispatcher.dispatch('message', { text: 'An event’ }) } </script> <button on:click={handleEvent}>Fire event</button> // App.svelte <script> import Inner from './Inner.svelte'; function handleMessage(event) { alert(event.detail.text); } </script> <Inner on:message={handleMessage}/> Dispatch, to raise the event Name of the event Name of the event Handle event
  21. 21. Forwarding an event Inner Outer App message messagemessage // Inner.svelte <script> import { createEventDispatcher } from 'svelte'; let dispatcher = createEventDispatcher(); function handleEvent() { dispatcher.dispatch('message', { text: 'An event’ }) } </script> <button on:click={handleEvent}>Fire event</button> // Outer.svelte <Inner on:message /> // App.svelte import Outer from './Outer.svelte'; function handleEvent(event) { console.log(event.text); } </script> <Outer on:message={handleEvent} /> No event handler Just forward the event
  22. 22. Directives • IF ELSE, IF ELSE-IF ELSE • FOR • ASYNC/AWAIT
  23. 23. IF/ELSE {#if user.loggedIn} <button on:click={toggle}> Log out </button> {:else} <button on:click={toggle}> Log in </button> {/if} {#if <boolean>} {:else <boolean>} {/if}
  24. 24. IF/ELSE-IF/ELSE {#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 <boolean>} {:else if <boolean>} :else <boolean>} {/if
  25. 25. FOR-LOOPS {#each todos as todo} <div>{todo.name}</div> {/each} {#each todos as { name, id, done }} <div>{todo.name}</div> {/each} {#each todos as todo(todo.id)} // do stuff {/each} list item We can spread it Point out the unique property, so it doesn’t do unnecessary work
  26. 26. AWAIT BLOCK <script> let promise = getData(); function async getData() { const res = await fetch(url); const json = await res.json(); return json; } </script> <div> {#await promise} <p>...loading</p> {:then data} <p>Render data {data}</p> {:catch error} <p>Error {error.message}</p> {/await} </div> Construct the Promise. We can capture the error and rethrow if we need to Wait for the Promise Render data Render error
  27. 27. Binding <script> let name = 'render me’; Let yes = false; </script> <input bind:value={name} /> <input type=number bind:value={a} min=0 max=10> <input type=checkbox bind:checked={yes}> Coerces the string to be a number Binds to variable yes Binds input to variable name
  28. 28. Grouped bindings <script> let menu = [ 'Cookies and cream’, 'Mint choc chip’, 'Raspberry ripple’ ]; let flavours = []; let selectedFlavour = ''; </script> <h2>Pick many</h2> {#each menu as flavour} <label> <input type=checkbox bind:group={flavours} value={flavour}> {flavour} </label> {/each} <h2>Pick one</h2> {#each menu as flavour} <label> <input type=radio bind:group={selectedFlavour} value={flavour}> {flavour} </label> {/each} <div> Selected Flavours: {flavours} </div> <div> Selected flavour: {selectedFlavour} Checkbox binds to array [] Radio binds to string
  29. 29. Other controls <textarea bind:value={value}></textarea> <select bind:value={selected} on:change="{() => answer = ''}"> {#each questions as question} <option value={question}> {question.text} </option> {/each} </select> <div contenteditable="true" bind:innerHTML={html} ></div> Bind value to <select> Bind to innerHTML Loop out items
  30. 30. Working with lists (e.g TodoList) • Mutate list bind:checked • Immutable list, events
  31. 31. Mutating list with bind:checked <script> let todos = [ { done: false, text: 'finish Svelte tutorial' }, { done: false, text: 'build an app' }, { done: false, text: 'world domination' } ]; function add() { todos = todos.concat({ done: false, text: '' }); } function clear() { todos = todos.filter(t => !t.done); } $: remaining = todos.filter(t => !t.done).length; </script> <style> .done { opacity: 0.4; } </style> <h1>Todos</h1> {#each todos as todo} <div class:done={todo.done}> <input type=checkbox bind:checked={todo.done} > <input placeholder="What needs to be done?" bind:value={todo.text} > </div> {/each} <p>{remaining} remaining</p> <button on:click={add}>Add new</button> <button on:click={clear}>Clear completed</button> {#each todos as todo} <div>{todo.text} {todo.done}</div> {/each}
  32. 32. Immutable list, event <script> let todos = [ { done: false, text: 'finish Svelte tutorial' }, { done: false, text: 'build an app' }, { done: false, text: 'world domination' } ]; function add() { todos = todos.concat({ done: false, text: '' }); } function handleChanged(e, todo) { console.log('changed', e.target.checked); console.log('changed todo', todo) } function clear() { todos = todos.filter(t => !t.done); } $: remaining = todos.filter(t => !t.done).length; </script> <style> .done { opacity: 0.4; } </style> <h1>Todos</h1> {#each todos as todo} <div class:done={todo.done}> <input type=checkbox on:change={(e) => handleChanged(e, todo)} checked={todo.done} > <input placeholder="What needs to be done?" bind:value={todo.text} > </div> {/each} <p>{remaining} remaining</p> <button on:click={add}>Add new</button> <button on:click={clear}>Clear completed</button> {#each todos as todo} <div>{todo.text} {todo.done}</div> {/each}
  33. 33. Routing https://github.com/EmilTholin/svelte-routing <script> import Test from './Test.svelte'; export let name; import { Router, Link, Route } from "svelte-routing"; import Home from "./routes/Home.svelte"; import About from "./routes/About.svelte"; import Blog from "./routes/Blog.svelte"; export let url = ""; </script> <main> <Router url="{url}"> <nav> <Link to="/">Home</Link> <Link to="about">About</Link> <Link to="blog">Blog</Link> </nav> <div> <Route path="blog" component="{Blog}" /> <Route path="about" component="{About}" /> <Route path="/"> <Home /> </Route> </div> </Router> </main> npm install --save svelte-routing
  34. 34. Testing - https://testing-library.com/docs/svelte- testing-library/setup Svelte Testing Library npm install --save-dev @testing-library/svelte Jest is recommended with it npm install --save-dev jest // package.json { "scripts": { "test": "jest src", "test:watch": "npm run test -- --watch" } } npm install --save-dev svelte-jester // package.json { "jest": { "transform": { "^.+.svelte$": "svelte-jester" }, "moduleFileExtensions": ["js", "svelte”] } }
  35. 35. A test // NOTE: jest-dom adds handy assertions to Jest and it is recommended, but not required. import '@testing-library/jest-dom/extend-expect’ import { render, fireEvent } from '@testing-library/svelte’ import Comp from '../Comp’ test('shows proper heading when rendered', () => { const { getByText } = render(Comp, { name: 'World' }) expect(getByText('Hello World!')).toBeInTheDocument() }) // Note: This is as an async test as we are using `fireEvent` test('changes button text on click', async () => { const { getByText } = render(Comp, { name: 'World' }) const button = getByText('Button’) // Using await when firing events is unique to the svelte testing library because // we have to wait for the next `tick` so that Svelte flushes all pending state changes. await fireEvent.click(button) expect(button).toHaveTextContent('Button Clicked') })
  36. 36. Summary • Been around a while Created in 2016, now on version 3 • Is the new kid on the Block, yet another SPA framework? • Easy to learn, its’ just HTML, CSS and JS • Is a Compiler, so a framework at compile time • Small footprint & fast no VDOM, at least as fast as the big three SPAs • Tooling for VIM, VS Code • Dependent on OSS libs, Router (3rd party) + Test lib in place • Remaining questions, Ready for production? Should I use in production?

×