
目次
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>