Vue3 TODOアプリ入門|リロードしても消えない仕組みをlocalStorageで完全解説

TOC
〜リロードしても消えない仕組みを理解する〜
VueでTODOアプリを作れるようになると、
次に必ずぶつかる壁があります。
「ページを更新すると全部消える問題」
これはバグではありません。
「保存していない」だけです。
この記事では、
- なぜデータが消えるのか
- ブラウザに保存するとはどういうことか
- Vue3でどう実装するのか
を 超噛み砕いて解説します。
なぜTODOは消えるのか?
まず前提。
const todos = ref([]);
これは メモリ上のデータです。
- ページを閉じる
- リロードする
👉 メモリはリセットされる
だから消える。
解決策:ブラウザに保存する
サーバーを使わなくても、
ブラウザ自身にデータを保存する仕組みがあります。
それが localStorage。
localStorageとは?
一言で言うと
👉 ブラウザにある「小さな引き出し」
特徴:
- ページを閉じても残る
- サーバー不要
- 文字列しか保存できない
超シンプルな例(生JavaScript)
localStorage.setItem("message", "こんにちは");
localStorage.getItem("message");
// → "こんにちは"
重要:そのままでは配列は保存できない
TODOは配列+オブジェクトです。
[
{ id: 1, text: "勉強する", done: false }
]
localStorageは
❌ 配列を直接保存できない
そこで使うのが JSON。
JSONとは?
保存用の「文字列フォーマット」
JSON.stringify(配列やオブジェクト)
JSON.parse(文字列)
例
const data = [{ text: "勉強" }];
const string = JSON.stringify(data);
// "[{"text":"勉強"}]"
JSON.parse(string);
// 元の配列に戻る
Vue3での実装(全体像)
これからやることは3つだけ👇
- TODOが変わったら保存する
- 保存するときは JSON にする
- ページ表示時に復元する
script setup(ロジック部分)
<script setup>
import { ref, watch, onMounted } from "vue";
const text = ref("");
const todos = ref([]);
TODOを追加する
const addTodo = () => {
if (!text.value.trim()) return;
todos.value.push({
id: Date.now(),
text: text.value,
done: false
});
text.value = "";
};
ポイント
refの中身は.value- 配列に push すると Vue が反応する
- id は「そのTODO自身を識別するため」
削除する(indexは使わない)
const removeTodo = (id) => {
todos.value = todos.value.filter(todo => todo.id !== id);
};
なぜ filter?
- 新しい配列を作る
- Vueが変更を確実に検知できる
- indexに依存しない(安全)
完了状態を切り替える
const toggleTodo = (id) => {
const todo = todos.value.find(todo => todo.id === id);
if (todo) {
todo.done = !todo.done;
}
};
ここが核心:watchで自動保存
watchとは?
👉 「値が変わった瞬間を監視する」機能
todos が変わったら保存する
watch(
todos,
(newTodos) => {
localStorage.setItem(
"todos",
JSON.stringify(newTodos)
);
},
{ deep: true }
);
なぜ deep: true が必要?
todos の中身はこう👇
[
{ text: "勉強", done: false }
]
- 配列の中に
- オブジェクトがある
done が変わっただけでは
通常の watch では検知されない
👉 深い変更も見る = deep
ページ表示時に復元する
onMounted(() => {
const saved = localStorage.getItem("todos");
if (saved) {
todos.value = JSON.parse(saved);
}
});
onMountedとは?
👉 画面が表示された直後に一度だけ実行される
用途:
- 初期データ取得
- localStorage復元
- API呼び出し
template(完成版)
<template>
<div>
<input v-model="text" />
<button @click="addTodo">追加</button>
<ul>
<li v-for="todo in todos" :key="todo.id">
<span
@click="toggleTodo(todo.id)"
:style="{ textDecoration: todo.done ? 'line-through' : 'none' }"
>
{{ todo.text }}
</span>
<button @click="removeTodo(todo.id)">削除</button>
</li>
</ul>
</div>
</template>
何ができるようになったか?
この時点であなたは👇
- Vueのリアクティブを理解
- 状態が「どこにあるか」分かる
- データを永続化できる
つまり、
「チュートリアル卒業」
よくある誤解
❌ localStorage = Vueの機能
⭕ ただのブラウザ機能
❌ watch = 難しい
⭕ 「変わったらやる」だけ
関連記事紹介
Programming Notes


【Vue3入門】TODOアプリで理解するcomputedと表示切り替え|DOM操作しない理由を解説
Vue3のcomputedがよく分からない初心者向けに、TODOアプリを使って「表示切り替え」と自動更新の仕組みを解説。DOM操作をしなくても画面が変わる理由がスッと理解できます…
Programming Notes


Vue3 TODOアプリ実践|v-forでkeyにindexを使ってはいけない理由
Vue3でTODOアプリを作りながら、v-forでkeyにindexを使ってはいけない理由を解説。idを使った正しい設計と、実務で通用するVue3の書き方が理解できます。
その他関連記事はこちら→Programming Notes

Comments