Skip to content

Latest commit

 

History

History
538 lines (463 loc) · 26.7 KB

learning-svelte-and -sveltekit.md

File metadata and controls

538 lines (463 loc) · 26.7 KB

Learning Svelte and SvelteKit

Introduction

Reactivity

  • Assignments - <button on:click={increment}> </button>

  • Declarations - $: doubled = count * 2

  • Statements:

    • $: console.log()
    • $: { console.log() }
    • $: if (count >= 10) { }
  • Update arrays and objects:

    • () => numbers = [...numbers, numbers.length + 1];

    • () => numbers[numbers.length] = numbers.length + 1;

    • Name of updated variable must appear on the left hand side of the assignment.

      const obj = { foo: { bar: 1 } };
      const foo = obj.foo;
      foo.bar = 2;
      
      // instead
      const obj = { foo: { bar: 1 } };
      obj.foo.bar = 2;

Props

Logic

  • If blocks - {#if count > 10} .. {/if}
  • Else blocks - {#if count > 10} .. {:else} .. {/if}
  • Else-if blocks - {#if count > 10} .. {:else if count < 5} .. {:else} .. {/if}
  • Each blocks:
    • {#each colors as color} .. {/each}
    • {#each colors as color, i} .. {/each}
  • Keyed each blocks - {#each things as thing (thing.id)} .. {/each}
  • Await blocks:
    • {#await promise} .. {:then value} .. {:catch error} .. {/await}
    • {#await promise then number} .. {/await}

Events

  • DOM events - <div on:pointermove={handleMove}> .. </div>
  • Inline handlers - <div on:pointermove={(e) => .. }> .. </div>
  • Event modifiers - <button on:click|once={() => alert(clicked)}>
    • List of modifiers:
      • preventDefault
      • stopPropagation
      • passive
      • nonpassive
      • capture
      • once
      • self
      • trusted
    • Chaining modifiers - on:click|once|capture={ .. }
  • Component events:
    • Create event dispatcher
      <script>
        import { createEventDispatcher } from "svelte";
        const dispatch = createEventDispatcher();
      </script>
    • <Inner on:message={handleMessage}} />
  • Event forwarding - <Inner on:message />
  • DOM event forwarding - <button on:click> Push </button>

Bindings

  • Data flow in Svelte is top down.
  • Text inputs - <input bind:value={name}>
  • Numeric inputs:
    • <input type="number" bind:value={a} min="0" max="10" />
    • <input type="range" bind:value={a} min="0" max="10" />
  • Checkbox inputs - <input type="checkbox" bind:checked={yes}>
  • Select bindings - <select bind:value={selected}>
  • Group inputs:
    • <input type="radio" name="scoops" value={number} bind:group={scoops} />
    • <input type="checkbox" name="colors" value={color} bind:group={colors} />
  • Select multiple - <select multiple bind:value={flavors}> .. </select>
  • Textarea inputs:
    • <textarea bind:value={value}> </textarea>
    • <textarea bind:value></textarea>

Lifecycle

  • onMount- runs after component is first rendered to the DOM.
  • beforeUpdate and afterUpdate:
    • before and after DOM is updated.
    • beforeUpdate runs before onMount.
  • tick - returns and resolves a promise:
    • after state changes have been applied to the DOM.
    • immediately, if there are no pending state changes.

Stores

  • Writable stores - const count = writable(0);
    • count.update((n) => n + 1);
    • count.set(0);
  • Auto-subscriptions:
    • onDestroy(unsubscribe);
    • reference a store with $. Example,{$count}
  • Readable stores - readable(initialValue, function)
  • Derived stores - derived(store, function)
  • Custom stores:
    • a store only needs to correctly implement the subscribe method.
    • can add custom methods to the store.
  • Store bindings - you can bind writable stores (has a set method).
    • <input bind:value={$name}>
    • <button on:click={() => $name += '!'}> </button>

Motion

  • Tweens - gradual changes (progress bar).
    • Options:
      • delay
      • duration
      • easing - p => t function
      • interpolate
  • Springs - frequent changes

Transitions

Animations

  • Flip - "First, Last, Invert, Play", animate:flip.

Actions

  • Use - element-level lifecycle functions, <div class="menu" use:trapFocus>.
  • Adding parameters - <button use:tooltip={{ content }}> </button>

Advanced bindings

  • Contenteditable bindings - <div bind:innerHTML={html} contenteditable />
    • contenteditable attribute support:
      • textContent
      • innerHTML
  • Each block bindings - bind properties inside an each block.
  • Media elements - bind properties of <audio> and <video> elements.
    • readonly bindings:
      • duration - total duration of video in seconds
      • buffered - array of {start, end} objects
      • seekable - ditto
      • played - ditto
      • seeking - boolean
      • ended - boolean
      • readyState - number between 0 and 4
      • videoWidth - for video only
      • videoHeght - for video only
    • two-way bindings:
      • currentTime - current point in the video, in seconds
      • playbackRate - how fast to play the video, 1 is normal
      • paused
      • volume - value between 0 and 1
      • muted - boolean
  • Dimensions - bind block-level element:
    • clientWidth
    • clientHeight
    • offsetWidth
    • offsetHeight
  • This - bind:this
  • Component bindings - bind to component props.
  • Binding to component instances - bind to component instances with bind:this.

Classes and styles

  • Classes:
    • <button class="card {flipped ? 'flipped' : ''}>
    • <button class="card" class:flipped={flipped}>
  • Shorthand Class - <button class="card" class:flipped>
  • Styles - inline style attributes.
    • <button class="card" style:--bg-1="palegoldenrod">
  • Component styles - influence styles inside a child component.
    • :global should be used as a last resort.
    • <style> .box { background-color: var(--color, #ddd); } </style>
    • <Box --color="red" />

Component composition

  • Slots - components can have children.
    • <div class="card"> <slot /> </div>
    • <Card> <span>Patrick BATEMAN</span> </Card>
  • Named slots - <div class="card"> <slot name="company" /> </div>
  • Slot fallbacks - <slot name="telephone"> telephone </slot>
  • Slot props - <slot {item} />
  • Checking for slot content - {#if $$slots.header} .. {/if}

Context API

Special elements

  • svelte:self - allows a component to contain itself recursively.
  • svelte:component - instead of if blocks, create a single dynamic component.
  • svelte:element - instead of if blocks, create a single dynamic element.
  • svelte:window - add event listeners to the window object.
  • svelte:window bindings - <svelte:window bind:scrollY={y} />
    • List of properties you can bind:
      • innerWidth
      • innerHeight
      • outerWidth
      • outerHieght
      • scrollX
      • scrollY
      • online - an alias for window.navigator.onLine
    • All properties are readonly except scrollX and scrollY.
  • svelte:body - add event listeners to document.body.
  • svelte:document - add event listeners to document.
    • Avoid mouseenter and mouseleave handlers here.
    • Use <svelte:body> instead.
  • svelte:head - insert elements inside <head> of your document.
    • Useful for thins like <title> and <meta> tags for good SEO.
    • In SSR mode, contents of <svelte:head> are returned separately from your HTML.
  • svelte:options - specify compiler options.
    • <svelte:options immutable/>.
    • Available options:
      • immutable={true}
      • immutable={false} - default.
      • accessors={true}
      • accessors={false} - default.
      • namespace="..."
      • customElement="...
  • svelte:fragment - place content in a named slot without wrapping it in a container DOM element.

Module Context

  • Sharing code - <script context="module> </script>
    • Code will run once when the module first evaluates.
    • Components talk to each other without any state management.
  • Exports - you can't have a default export, because the component is the default export.

Miscellaneous

  • Debug:
    • Inspect logs - console.log(...)
    • Pause execution - (@debug ...}

SvelteKit's main job boils down to three things:

  1. Routing - figure out which route matches an incoming request
  2. Loading - get the data needed by the route
  3. Rendering - generate HTML (on the server) or update the DOM (in the browser)

Routing

  • Pages - filesystem-based routing, meaning routes are defined by folders.
    • Every +page.svelte file inside src/routes creates a page.
    • Navigation is like a single-page app.
  • Layouts - share UI between pages with +layout.svelte.
    • +layout.svelte files apply to child and sibling routes.
  • Route parameters - use square brackets around a valid variable name,
    • src/routes/blog/[slug]/+page.svelte
    • For multiple route parameters in one segment: foo/[bar]x[baz]

Loading data

  • Page data - declare load function with +page.server.js.
  • Layout data - +layout.server.js loads data for every child route.

Headers and cookies

  • Setting headers - set response headers with setHeaders.
    • Commonly used for customizing caching behavior with Cache-Control.
  • Reading and writing cookies:
    • read cookies with cookies.get(name, options);.
    • write cookies with cookies.set(name, value, options);.
      • explicitly configure path since browsers set it on the parent of current path.
    • SvelteKit sets the following defaults:
      • httpOnly: true
      • secure: true
      • sameSite: lax

Shared modules

  • The $lib alias - anything inside src/lib can be accessed using $lib.
    • You can use $lib as long as the module is in the src folder.
    • "Put code close to where it's used".

Forms

  • The form element - <form method="POST"> </form>
  • Named form actions - <form method="POST" action"?/create"> </form>
    • Most pages need multiple actions.
    • Default actions cannot coexist with named actions.
  • Validation:
    • HTML form validation like required, and so on.
    • Server-side validation.
  • Progressive enhancement - enhance experience with JavaScript, use:enhance.
    • App should still be usable when JavaScript is disabled in browsers.
  • Customize use:enhance - customize by providing callback, use:enhance={() => {}}.
    • Useful for adding pending states and optimistic UI.

API Routes

  • General info:
    • Uses +server.js for API route handlers.
    • Request handlers must return a Response object.
    • Generating responses with SvelteKit, use json(data).
  • GET handlers
  • POST handlers:
    • Use form actions for most cases, less code and works without JavaScript.
    • Only mutate data in a way that you'd get the same result by reloading the page.
  • Other handlers: PUT, DELETE, and so on.

Stores

  • SvelteKit makes three readonly stores available via $app/stores module:
    • page
    • navigating
    • updated
  • Page:
    • url - the URL of current page
    • params - the current page's parameters
    • route - object with id representing the current route
    • status - the HTTP status code of the current page
    • error - the error object of the current page
    • data - the data of the current page with return values of all load functions
    • form - the data returned from a form action.
  • Navigating - represents the current navigation.
    • from and to objects with the following properties:
      • params
      • route
      • url
    • type - the type of navigation:
      • link
      • popstate
      • goto
  • Updated - true/false, if a new app version has been deployed since opening the page.
    • Manually check for new versions, updated.check().

Errors and redirects

  • Basics - two types of errors; expected and unexpected errors.
    • Expected errors:
      • Created with @sveltejs/kit error helper.
      • Displays expected error message.
    • Unexpected errors:
      • Any other error like throw new Error().
      • Displays generic Internal Error.
      • Can contain sensitive data.
  • Error pages - customize error page by using +error.svelte.
    • SvelteKit renders an error page on load function errors.
  • Fallback errors - customize fallback error page in src/error.html file.
    • Can include:
      • %sveltekit.status% - the HTTP status code
      • %sveltekit.error.message% - the error message
  • Redirects - use throw redirect(...) to redirect from one page to another.
    • You can use throw redirect(...) inside:
      • load functions
      • form actions,
      • API routes
      • handle hook.
    • Common status codes:
      • 303 - for form actions after successful submission
      • 307 - for temporary redirects
      • 308 - for permanent redirects

Hooks

  • handle - receives an event object and a resolve function, returns aResponse object.
    • in src/hooks.server.js file.
  • RequestEvent - event object passed into handle.
    • Passed into API routes, form actions, and load functions.
    • Properties and methods:
      • cookies
      • fetch
      • getClientAddress()
      • isDataRequest
      • locals
      • params
      • request
      • route
      • setHeaders(...)
      • url
  • handleFetch - event object's fetch method.
    • Inherits the cookie and authorization headers from the incoming request.
    • Can make relative requests on the server.
    • Internal requests go directly to the handler function when running on the server.
  • handleError - intercept unexpected errors.

Page options

  • Basics:
    • export page options from the following:
      • +page.js
      • +page.server.js
      • +layout.js
      • +layout.server.js
    • Available options:
      • ssr - whether or not pages should be server-rendered
      • csr - whether to load the SvelteKit client
      • prerender - whether to prerender pages at build time, instead of per-request
      • trailingSlash - whether to strip, add, or ignore trailing slashes in URLs
    • Options apply to individual pages if exported from +page.js or +page.server.js.
    • Options apply to group of pages if exported from +layout.js or +layout.server.js.
    • Export from root layout to define an option for the whole app.
    • Child layouts and pages override values from parent.
  • ssr - generates HTML on the server.
    • SvelteKit does SSR by default.
    • Improves performance, resilience, and SEO.
    • Setting ssr to false in the root +layout.server.js turns the app into a SPA.
  • csr - makes pages interactive and update on navigation without a full-page reload.
  • prerender - generate HTML for a page once, at build time.
    • Serving static data is cheap and performant.
    • Serve large number of users without worrying about cache-control headers.
    • Prerendered pages:
      • must not contain form actions.
      • must get the same content from the server.
    • Setting prerenter to true in the root +layout.server.js turns SvelteKit to SSG.
  • trailingSlash:
    • trailing URL slashes might harm SEO.
    • default value is never.
    • add trailing slash with always.
    • support both cases with ignore.

Link Options

  • Preloading - preload data or JavaScript to speed up page loads.
    • data-sveltekit-preload-data
      • hover - default, falls back to tap on mobile
      • tap - begin preloading on tap
      • off - disable preloading
    • data-sveltekit-preload-code
      • eager - preload everything on the page
      • viewport - preload everything as it appears in the viewport
      • hover - default
      • tap
      • off
    • preloading programmatically, import from $app/navigation:
      • preloadData('/foo')
      • preloadCode('/bar')
  • Reloading the page - data-sveltekit-reload, to reload when navigating between pages.

Advanced routing

  • Optional parameters - use double brackets for optional parameters, [[foo]].
  • Rest parameters - for matching an unknown number of path segments, use [...rest].
    • More specific routes will be tested first, making rest parameters as catch-all routes.
  • Param matchers - validate route params with matchers, `/colors/[color=matcher].
    • Matchers run both on the server and in the browser.
  • Route groups - group routes using parenthesis, (authed)/app/+page.svelte.
    • Useful for grouping routes that need authentication.
    • Use (authed)/+layout.server.js to control grouped routes behavior.
    • Use (authed)/+layout.svelte to customize grouped routes UI.
  • Breaking out of layouts - break from layout hierarchy by adding @, [email protected].
    • src/routes/a/b/c/+page.svelte inherits every layout above it:
      • src/routes/+layout.svelte
      • src/routes/a/+layout.svelte
      • src/routes/a/b/+layout.svelte
      • src/routes/a/b/c/+layout.svelte
    • The root layout applies to every page of your app. You cannot break out of it.

Advanced loading

  • Universal load functions - turn server load functions into universal load functions.
    • Rename +page.server.js file to +page.js.
    • Functions run on the server during SSR, but also run in the browser when app hydrates.
    • Universal vs server docs.
    • Use cases for universal load functions:
      • loading data from external API
      • use in-memory data
      • delay navigation until image has been preloaded
      • return from load that can't be serialized
  • Using both load functions - access server data via the data property, data.message.
    • Data isn't merged, need to explicitly return message from universal load function.
  • Using parent data - access parent data from load functions with await parent().
    • Universal load functions can get data from a parent server load function.
    • A server load function can only get parent data from another server load function.
    • Fetch other data that is not dependent on parent data first.
  • Invalidation - invalidate specific or pattern URLs in load function with invalidate(...).
  • Custom dependencies - specify dependencies with depends(...).
    • Useful when not using fetch(url).
  • invalidateAll - re-run all load functions with invalidateAll().
    • invalidateAll() re-runs load functions without url dependencies.
    • invalidate(() => true) does not re-run load functions without url dependencies.

Environment Variables

  • General info:
    • static variables are known at build time.
      • import { API_KEY } from '$env/static/private, use variable: `API_KEY.
    • dynamic variables read values when the app runs.
      • import { env } from '$env/dynamic/private, use variable: env.API_KEY.
    • private variables Cannot import env variables into client-side code.
      • Can only import env variables into server modules:
        • +page.server.js
        • +layout.server.js
        • +server.js
        • any modules ending with .server.js
        • any modules inside src/lib/server
      • Server modules can only be imported by other server modules.
    • public variables can be safely exposed to the browser.
  • $env/static/private
  • $env/dynamic/private
  • $env/static/public
  • $env/dynamic/public