Svelte

Markets

Features

  1. No Virtual DOM
  2. Compiler/No base library
  3. Pure HTML/CSS/JS

Position

Runtime

Compiler

React

Svelte

Vue

Angular

Typescript

Babel ESNext

Pure JS

WebAssembly

Static analysis

AST/JIT

First look

// index.svelte

<script>
  import NextStepKeyVisual from './_components/NextStepKeyVisual.svelte';
  import EmploymentGoldCard from './_components/EmploymentGoldCard.svelte';
  import Workspace from './_components/Workspace.svelte';
  import Housing from './_components/Housing.svelte';
  import EntrepreneurVisa from './_components/EntrepreneurVisa.svelte';
</script>

<style lang="scss">
  .wrapper {
    width: 100%;
    max-width: 1280px;
    margin: 0 auto;
    padding: 80px 0 0 0;
  }
</style>

<div class="wrapper">
  <NextStepKeyVisual />
  <EmploymentGoldCard />
  <Workspace />
  <Housing />
  <EntrepreneurVisa />
</div>

SPA Routing

routify uses folder structure to define the router path

Template Condition

{#if porridge.temperature > 100}
  <p>too hot!</p>
{:else if 80 > porridge.temperature}
  <p>too cold!</p>
{:else}
  <p>just right!</p>
{/if}
<h1>Shopping list</h1>
<ul>
  {#each items as item}
    <li>{item.name} x {item.qty}</li>
  {/each}
</ul>
{#await promise}
  <!-- promise is pending -->
  <p>waiting for the promise to resolve...</p>
{:then value}
  <!-- promise was fulfilled -->
  <p>The value is {value}</p>
{:catch error}
  <!-- promise was rejected -->
  <p>Something went wrong: {error.message}</p>
{/await}
<button disabled="{number !== 42}">...</button>
<button disabled={!clickable}>...</button>

Event Handler/Binding

<script>
  let count = 0;

  function handleClick(event) {
    count += 1;
  }
</script>

<button on:click={handleClick}>
  count: {count}
</button>
<div
  bind:offsetWidth={width}
  bind:offsetHeight={height}>
    <Chart {width} {height} />
</div>
<script>
  import { onMount } from 'svelte';

  let canvasElement;

  onMount(() => {
    const ctx = canvasElement.getContext('2d');
    drawStuff(ctx);
  });
</script>

<canvas bind:this={canvasElement}></canvas>
<script>
  export let bar;

  function foo(node, bar) {
    // the node has been mounted in the DOM
    return {
      update(bar) {
        // the value of `bar` has changed
      },
      destroy() {
        // the node has been removed from the DOM
      }
    };
  }
</script>

<div use:foo={bar}></div>

Animation/Transition

transition = (node: HTMLElement, params: any) => {
  delay?: number,
  duration?: number,
  easing?: (t: number) => number,
  css?: (t: number, u: number) => string,
  tick?: (t: number, u: number) => void
}
animation = (node: HTMLElement, { from: DOMRect, to: DOMRect } , params: any) => {
  delay?: number,
  duration?: number,
  easing?: (t: number) => number,
  css?: (t: number, u: number) => string,
  tick?: (t: number, u: number) => void
}
<div in:fly out:fade>
  flies in, fades out
</div>

{#each list as item, index (item)}
  <li animate:flip="{{ delay: 500 }}">{item}</li>
{/each}

Lifecycle

onMount(callback: () => void)
beforeUpdate(callback: () => void)
afterUpdate(callback: () => void)
onDestroy(callback: () => void)

promise: Promise = tick()
setContext(key: any, context: any)
context: any = getContext(key: any)
hasContext: boolean = hasContext(key: any)
dispatch: ((name: string, detail?: any) => void) = createEventDispatcher();

Store

import { writable } from 'svelte/store';

const count = writable(0, () => {
  console.log('got a subscriber');
  return () => console.log('no more subscribers');
});

count.set(1); // does nothing

const unsubscribe = count.subscribe(value => {
  console.log(value);
}); // logs 'got a subscriber', then '1'

unsubscribe(); // logs 'no more subscribers'
import { readable } from 'svelte/store';

const time = readable(null, set => {
  set(new Date());

  const interval = setInterval(() => {
    set(new Date());
  }, 1000);

  return () => clearInterval(interval);
});
store = writable(value?: any, start?: (set: (value: any) => void) => () => void)
store = readable(value?: any, start?: (set: (value: any) => void) => () => void)
store = derived(a, callback: (a: any) => any)
store = derived([a, ...b], callback: ([a: any, ...b: any[]]) => any)

derived likes React's useMemo

So, the cons?

No Virtual DOM

  • You can not use dynamic JSX with variables easily. The template is static like Angular template, structure can not change largely like React.
  • Testing only support E2E Test.
  • Template driven's performance based on its static analyzer.
  • Still has lifecycle, shouldComponentUpdate...

No base library

  • Only has an advantage on a small project. When the project is larger, the base library bundle used low-percentage part in the bundle file.
  • SPA routing, data layer still need library. You only can remove react-dom/render and react virtual dom diff.

tsài-huē

Svelte

By Chia Yu Pai