現場1画面に集約されてしまっているため
機能ごとにページを分けていきます

ステップ2:ルーティングの設定
src
ディレクトリに router
フォルダを作成し、その中に index.js
ファイルを作成します。このファイルでルーティングの設定を行います。
src/router/index.js
の記述:
JavaScript
import { createRouter, createWebHistory } from "vue-router";
import PomodoroTimer from "../components/PomodoroTimer.vue";
import PomodoroCalendar from "../components/PomodoroCalendar.vue";
const routes = [
{
path: "/",
name: "Home",
component: PomodoroTimer,
},
{
path: "/calendar",
name: "Calendar",
component: PomodoroCalendar,
},
];
const router = createRouter({
history: createWebHistory(),
routes,
});
export default router;
解説:
createRouter
とcreateWebHistory
を Vue Router からインポートします。createWebHistory()
はブラウザの履歴 API を使用したルーティングモードです。PomodoroTimer.vue
とPomodoroCalendar.vue
をそれぞれのコンポーネントのパスからインポートします。routes
配列でパスとコンポーネントの対応を定義します。/
パスにはPomodoroTimer
コンポーネントを割り当て、名前をHome
とします。これがトップ画面になります。/calendar
パスにはPomodoroCalendar
コンポーネントを割り当て、名前をCalendar
とします。これがカレンダー画面になります。
createRouter
関数にhistory
とroutes
を渡してルーターインスタンスを作成します。- 作成したルーターインスタンスを
export default router;
でエクスポートします。
ステップ3:main.js
の修正
作成したルーターを Vue アプリケーションに組み込むために、src/main.js
を修正します。
src/main.js
の修正:
JavaScript
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router"; // ルーターをインポート
const app = createApp(App);
app.use(router); // ルーターを Vue アプリケーションに登録
app.mount("#app");
解説:
import router from './router';
で作成したルーターインスタンスをインポートします。app.use(router);
で Vue アプリケーションにルーターを登録します。これにより、<router-link>
や<router-view>
などのルーティング関連コンポーネントが使用できるようになります。
ステップ4:App.vue
の修正
App.vue
を修正して、ルーティングの切り替えを行うための <router-view>
コンポーネントと、画面遷移のためのリンク <router-link>
を追加します。
src/App.vue
の修正:
HTML
<template>
<div>
<nav>
<router-link to="/">タイマー</router-link> |
<router-link to="/calendar">カレンダー</router-link>
</nav>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App',
};
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
nav {
padding: 30px;
}
nav a {
font-weight: bold;
color: #2c3e50;
text-decoration: none;
}
nav a.router-link-exact-active {
color: #42b983;
}
</style>
1 解説:
<nav>
タグ内に、<router-link to="/">タイマー</router-link>
と<router-link to="/calendar">カレンダー</router-link>
を追加しました。<router-link to="...">
は、指定されたパスへのリンクを生成するコンポーネントです。クリックすると、Vue Router が対応するコンポーネントを表示します。to="/"
: ルートパス(トップ画面)へのリンクを作成します。to="/calendar"
:/calendar
パス(カレンダー画面)へのリンクを作成します。
<router-view></router-view>
: 現在のルートに対応するコンポーネントがレンダリングされる場所です。URL が/
であればPomodoroTimer
が、/calendar
であればPomodoroCalendar
がここに表示されます。<style>
タグでは、ナビゲーションのスタイルと、アクティブなリンクのスタイルを設定しています。
上記の編集を行ったことでページを遷移できるようになった
追加 -カレンダー画面でタイマーを非表示にする
修正方針:
App.vue
で作業終了時に受け取ったログをcalendarWorkLogs
に保持する。PomodoroCalendar
でprops
のworkLogs
を監視し、変更があった場合に内部のcalendarWorkLogs
を更新する。 これにより、App.vue
から渡された新しいログがカレンダーに反映されます。
追加前のコード (App.vue
の <script>
部分):
JavaScript
import PomodoroTimer from "./components/PomodoroTimer.vue";
import PomodoroCalendar from "./components/PomodoroCalendar.vue";
import { useRoute } from "vue-router";
import { ref } from "vue";
export default {
name: "App",
components: {
PomodoroTimer,
PomodoroCalendar,
},
setup() {
const route = useRoute();
const calendarRef = ref(null); // PomodoroCalendar コンポーネントの参照
const calendarWorkLogs = ref([]); // (一旦保持用として残しますが、直接メソッドを呼ぶため必須ではありません)
const handleWorkFinished = (logData) => {
console.log("App.vue で作業終了イベントを受け取りました:", logData);
if (route.path === "/calendar" && calendarRef.value) {
calendarRef.value.addWorkLog(logData); // カレンダーコンポーネントの addWorkLog を直接呼び出す
} else {
calendarWorkLogs.value.push(logData); // カレンダーが非表示の場合は一旦保持
}
};
return { route, calendarRef, calendarWorkLogs, handleWorkFinished };
},
mounted() {
// ページロード時や画面遷移時に、保持しているログをカレンダーに反映
this.$watch(
() => this.$route.path,
(newPath) => {
if (
newPath === "/calendar" &&
this.calendarRef &&
this.calendarWorkLogs.value.length > 0
) {
this.calendarWorkLogs.value.forEach((log) => {
this.calendarRef.addWorkLog(log);
});
this.calendarWorkLogs.value = []; // 反映後にクリア
}
},
{} // ウォッチャーオプション(今回は不要ですが、Prettierの整形に合わせて記述)
);
},
};
追加後のコード (App.vue
の <script>
部分):
JavaScript
import PomodoroTimer from "./components/PomodoroTimer.vue";
import PomodoroCalendar from "./components/PomodoroCalendar.vue";
import { useRoute } from "vue-router";
import { ref } from "vue";
export default {
name: "App",
components: {
PomodoroTimer,
PomodoroCalendar,
},
setup() {
const route = useRoute();
const calendarWorkLogs = ref([]); // カレンダーに渡す作業ログ
const handleWorkFinished = (logData) => {
console.log("App.vue で作業終了イベントを受け取りました:", logData);
calendarWorkLogs.value.push(logData); // 作業終了時にログを calendarWorkLogs に追加
};
return { route, calendarWorkLogs, handleWorkFinished };
},
};
追加後のコード (PomodoroCalendar.vue
の <script>
部分):
JavaScript
export default {
name: "PomodoroCalendar",
props: {
workLogs: {
type: Array,
default: () => [],
},
},
data() {
const today = new Date();
const currentYear = today.getFullYear();
const currentMonth = today.getMonth() + 1;
return {
year: currentYear,
month: currentMonth,
daysOfWeek: ["日", "月", "火", "水", "木", "金", "土"],
todayDate: today.getDate(),
todayMonth: currentMonth,
todayYear: currentYear,
calendarWorkLogs: [...this.workLogs], // props の値を初期値として使用
};
},
watch: {
workLogs(newWorkLogs) {
this.calendarWorkLogs = [...newWorkLogs]; // props が更新されたら data も更新
},
},
computed: {
daysInMonth() {
return new Date(this.year, this.month, 0).getDate();
},
firstDayOfMonth() {
return new Date(this.year, this.month - 1, 1).getDay();
},
calendarGrid() {
const grid = [];
let week = [];
let dayCounter = 1;
for (let i = 0; i < this.firstDayOfMonth; i++) {
week.push(null);
}
while (dayCounter <= this.daysInMonth) {
week.push(new Date(this.year, this.month - 1, dayCounter));
if (week.length === 7) {
grid.push(week);
week = [];
}
dayCounter++;
}
while (week.length < 7 && week.length > 0) {
week.push(null);
}
if (week.length > 0) {
grid.push(week);
}
return grid;
},
},
methods: {
isToday(date) {
return (
date &&
date.getDate() === this.todayDate &&
date.getMonth() + 1 === this.todayMonth &&
date.getFullYear() === this.todayYear
);
},
getWorkLogsForDate(date) {
if (!date) {
return [];
}
const formattedDate = `${date.getFullYear()}-${(date.getMonth() + 1)
.toString()
.padStart(2, "0")}-${date.getDate().toString().padStart(2, "0")}`;
return this.calendarWorkLogs.filter((log) => log.date === formattedDate);
},
},
};
</script>
追加後の変更点と説明:
1. App.vue
の <script>
の変更:
- 変更前:
handleWorkFinished
でカレンダーが表示されている場合にcalendarRef.value.addWorkLog(logData)
を直接呼び出し、そうでない場合はcalendarWorkLogs.value.push(logData)
で保持していました。また、mounted
フックで画面遷移時に保持しているログをカレンダーに反映しようとしていました。 - 変更後:
handleWorkFinished
では、常に 受け取ったlogData
をcalendarWorkLogs.value.push(logData)
で保持するように変更しました。カレンダーが表示されているかどうかに関わらず、作業ログはcalendarWorkLogs
に蓄積されます。mounted
フックとその関連コードを削除しました。
変更の理由:
- カレンダーコンポーネントのメソッドを親コンポーネントから直接呼び出すのは、コンポーネント間の依存性を高めるため、よりデータバインディングの仕組みに沿った形に変更します。
- 画面遷移時にログを反映する処理は、
props
とwatch
を使うことでよりリアクティブに実現できます。
2. PomodoroCalendar.vue
の <script>
の変更:
- 変更前:
props
でworkLogs
を受け取っていましたが、その値が変更されても自動的にカレンダーの表示は更新されませんでした。 - 変更後:
watch
オプションを追加し、workLogs
props が変更されたときに、内部のcalendarWorkLogs
を[...newWorkLogs]
で更新するようにしました。
追加の理由:
props
は親コンポーネントから子コンポーネントへデータを渡すための仕組みです。App.vue
のcalendarWorkLogs
が更新されると、PomodoroCalendar
のworkLogs
props も自動的に更新されます。watch
を使うことで、workLogs
props の変更を監視し、変更があった場合にカレンダーの表示に必要なcalendarWorkLogs
を更新することで、画面がリアクティブに更新されます。
これらの変更を加えることで、タイマーが終了すると App.vue
の calendarWorkLogs
が更新され、その変更が PomodoroCalendar
の workLogs
props を通じて監視され、カレンダーの表示が更新されるようになります。