
目次
Svelteをインストール
まず、以下のコマンドでVite + Svelteをインストールして作業環境を作成します。
npm create vite@latest svelte-todo -- --template svelte cd svelte-todo npm install
HTML, CSSのコーディング
まずapp.cssの内容をすべて削除して、App.svelteを以下の内容に変更する。
<script>
</script>
<main>
  <input type="text" value=""> <button>追加</button>
  <hr>
  <ul class="todo">
    <li>
      <button>削除</button>
      <span>foo</span>
    </li>
    <li>
      <button>削除</button>
      <span>bar</span>
    </li>
  </ul>
</main>
<style>
  .todo > li + li {
    margin-top: 0.5em;
  }
</style>
npm run devを実行してブラウザで確認すると下図のように表示されます。

テキストを入力して「追加」を押すとTodoリストに追加され、「削除」を押すと削除されるようにします。
liタグ部分を配列から出力
liタグで表示している部分を配列にして {#each} を使用して表示します。
<script>
  let id = 1
  let todos = [
    {id: id++, text: 'foo'},
    {id: id++, text: 'bar'},
  ]
</script>
<main>
  <input type="text" value="">
  <button>追加</button>
  <hr>
  <ul class="todo">
    {#each todos as todo (todo.id)}
      <li>
        <button>削除</button>
        <span>{todo.text}</span>
      </li>
    {/each}
  </ul>
</main>
追加と削除の処理を追加
追加と削除の処理を入れます。
Enterキーでも追加処理ができるようformタグも追加しました。
<script>
  let id = 1
  let todos = [
    {id: id++, text: 'foo'},
    {id: id++, text: 'bar'},
  ]
  let todoText = ''
  function addTodo() {
    if (todoText.trim()) {
      todos = todos.concat({id: id++, text: todoText})
      todoText = ''
    }
  }
  function deleteTodo(todoId) {
    todos = todos.filter(todo => todo.id !== todoId)
  }
  function handleSubmit(e) {
    e.preventDefault()
  }
</script>
<main>
  <form on:submit={handleSubmit}>
    <input type="text" bind:value={todoText}>
    <button on:click={addTodo}>追加</button>
  </form>
  <hr>
  <ul class="todo">
    {#each todos as todo (todo.id)}
      <li>
        <button on:click={() => deleteTodo(todo.id)}>削除</button>
        <span>{todo.text}</span>
      </li>
    {/each}
  </ul>
</main>
以上で追加と削除の処理はできましたが、これだとデータを保存することができないので、FirebaseのFirestoreにTodoリストのデータの読み込み・追加・削除などができるようにします。
FirebaseのFirestoreから読み込み
まずFirebaseを使用するために以下のコマンドでfirebaseをインストールします。
npm i -D firebase
次にFirebaseのConsoleにアクセスしてプロジェクトを追加します。
https://console.firebase.google.com/u/0/?hl=ja

Googleアナリティクスは使わないのでチェックをはずしてください。

プロジェクトを追加したらアプリの追加(ウェブ)ボタンを押します。

アプリのニックネームの入力欄が表示されるので、「svelte-todo」と入力して「アプリを登録」を押します。

アプリを登録すると「Firebase SDK の追加」が表示されるので、赤枠のfirebaseConfigの値だけコピーしてFirebaseのConsoleに戻ります。

Consoleに戻ったらCloud Firestore (Firestore Database)に移動して「データベースの作成」を押します。
Cloud Firestoreのルールについて
「データベースの作成」を押したあと「本番環境モードで開始する」を押して、Cloud Firestoreのロケーションを「asia-northeast1 (Tokyo)」にして「有効にする」を押せばデータベースの作成は完了です。
Cloud Firestoreの本番のルールはデフォルトだと読み書き不可になっているため、読み込む場合は以下のようにルールのコードを変更してreadとwriteを許可する必要があります。
※下記のルールは単純にtrueにしてあるが、実際は条件式を入れる。
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if true;
    }
  }
}
コレクションを開始して残りのコードを記述
最後にコレクションを開始して残りのコードを記述する。
コレクションは「todo」にして、フィールドはidとtextを入れる。

あとはfirebaseから必要なものをimportして、以下のように読み込み、書き込み、削除の処理を書けばSvelteとFirebaseのFirestoreでTodoリストを作成できます。
<script>
  import { initializeApp } from 'firebase/app'
  import { getFirestore, collection, getDocs, addDoc, doc, deleteDoc } from 'firebase/firestore'
  const firebaseConfig = {
    apiKey: 'AIzaSyBfkn2r93JbZz8htZbQy2tsl1CZglJEtZk',
    authDomain: 'svelte-todo-d2e38.firebaseapp.com',
    projectId: 'svelte-todo-d2e38',
    storageBucket: 'svelte-todo-d2e38.appspot.com',
    messagingSenderId: '1040707526372',
    appId: '1:1040707526372:web:587d38291c804ecf4ad801'
  }
  const app = initializeApp(firebaseConfig)
  const db = getFirestore(app)
  const todoData = collection(db, 'todo')
  let id
  let todos = []
  let todoText = ''
  getDocs(todoData).then((query) => {
    query.forEach((doc) => {
      todos.push({ docId: doc.id, ...doc.data() })
    });
    todos = todos.sort((a, b) => a.id - b.id)
    id = Math.max(...todos.map(todo => todo.id))
    id = isFinite(id) ? id : 0
  })
  function addTodo(id) {
    if (todoText.trim()) {
      const data = {id: id++, text: todoText}
      todos = todos.concat(data)
      addDoc(todoData, data)
      todoText = ''
    }
  }
  function deleteTodo(docId) {
    todos = todos.filter(todo => todo.docId !== docId)
    const todoRef = doc(db, 'todo', docId)
    deleteDoc(todoRef)
  }
  function handleSubmit(e) {
    e.preventDefault()
  }
</script>
<main>
  <form on:submit={handleSubmit}>
    <input type="text" bind:value={todoText}>
    <button on:click={() => addTodo(++id)}>追加</button>
  </form>
  <hr>
  <ul class="todo">
    {#each todos as todo (todo.id)}
      <li>
        <button on:click={() => deleteTodo(todo.docId)}>削除</button>
        <span>{todo.text}</span>
      </li>
    {/each}
  </ul>
</main>
<style>
  .todo > li + li {
    margin-top: 0.5em;
  }
</style>



