SvelteでTodoをやってみるだけの回

公開:2021/02/17
更新:2021/02/18
4 min読了の目安(約3700字TECH技術記事

はじめに

Svelteやってみたいが何やろうってことで、まずはTodoを作ってみるだけの、ほんとにそれだけの回

Hello Todo!

コード

Todo.svelte

とりあえずこれだけでなんとなくできたので貼っておきます[1]

Todo.svelte
<script>
  let newTask = "";
  let todos = [];

  let newId = () => { return Date.now().toString(36) } 

  function add(task) {
    todos = [...todos, {id: newId(), name: task, done: false}] // ❶
  }

  function done(id) {
    console.log('done called')
    let result = todos.map(todo => {
      if (todo.id == id){ todo.done = true }
      return todo
    });
    todos = result; // ❷
  }

  $: activeTodos = todos.filter(function(todo){ // ❸
    return (todo.done == false)
  });
</script>

<main>
  <h1>HELLO TODO!</h1>
  <div class="container">
    <form>
      <div class="row">
        <div class="two-thirds column">
          <input class="u-full-width" type="text" placeholder="Add new task" bind:value={newTask}>
        </div>
        <div class="one-third column"><input type="button" value="add" on:click={add(newTask)}></div>
      </div>
    </form>
    {#if activeTodos.length === 0} <!---->
      <h3>YOU HAVE NO TASK!</h3>
    {:else}
      <table class="u-full-width">
        <thead>
          <tr>
            <th>ID</th>
            <th>Name</th>
            <th>Action</th>
          </tr>
        </thead>
        <tbody>
          {#each activeTodos as todo} <!---->
            <tr>
              <td>{todo.id}</td>
              <td>{todo.name}</td>
              <td><input type="button" value="done!" on:click={done(todo.id)}></td>
            </tr>
          {/each}
        </tbody>
      </table>
    {/if}
  </div>
</main>

<style>
  main {
    text-align: center;
    padding: 1em;
    max-width: 240px;
  }

  h1 {
    color: #ff3e00;
    text-transform: uppercase;
    font-size: 4em;
    font-weight: 100;
    margin-bottom: 1.5em;
  }

  h3 {
    color: gray;
    text-transform: uppercase;
    font-size: 2em;
    font-weight: 150;
    margin-top: 3.5em;
  }

  @media (min-width: 640px) {
    main {
      max-width: none;
    }
  }
</style>
  1. このtodos = [...todos, {id: newId(), name: task, done: false}]はイディオム的に使うと良い。Svelteでリアクティブな動作をさせるには条件があるようで[2]、たとえばtodo.push({id: newId(), name: task, done: false})だとリアクティブにならず、テーブルは更新されない
  2. これも❶と同様にリアクティブにするために代入している
  3. $1:のラベルをつけることで、リアクティブに再計算される。つまりtodosが変更されるとactiveTodosも再計算される
  4. テンプレートの中でif分が使えたり
  5. eachでループしたり

main.js

Todo.sveltemain.js側でimportしている

main.js
import Todo from './Todo.svelte';

const app = new Todo({
 target: document.body,
});

export default app;

index.html

index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset='utf-8'>
  <meta name='viewport' content='width=device-width,initial-scale=1'>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.min.css">
  <link rel="preconnect" href="https://fonts.gstatic.com">

  <title>Todo</title>

  <link rel='icon' type='image/png' href='/favicon.png'>
  <link rel='stylesheet' href='/global.css'>
  <link rel='stylesheet' href='/build/bundle.css'>

  <script defer src='/build/bundle.js'></script>
</head>
<body></body>
</html>

こんだけ。CSSはskelton.cssもつかっています

感想

Svelteは思ったよりVueっぽいぞ。Vueのシングルファイルコンポーネントそのままに思える。Vueやってた人は違和感なく入れると思う。また、一方でVueよりも生のJavascriptを触っている感が味わえてそこがまたよい。

脚注
  1. Svelteのシンタックスハイライトがほしい ↩︎

  2. Because Svelte's reactivity is triggered by assignments, using array methods like push and splice won't automatically cause updates. For example, clicking the button doesn't do anything ↩︎