久しぶりにVueを触っていたら、最近のスタンダードがVuexからPiniaに代わったということで少し試してみました。
以下に紹介してあるコードを少し修正しています。特にgettersを追加してみました。
BUILD A TO-DO LIST APP WITH PINIA AND VUE 3
コマンド
開発は以下のようなコマンドで行えます。
vue create todo
cd todo
npm install pinia
npm run serve
実行画面
ソースコード
ソースコードは以下の5ファイルになります。
- main.js
- store/useTodoListStore.js
- App.vue
- components/TodoList.vue
- components/TodoForm.vue
1.main.js
main.jsではuse(createPinia())でPiniaの使用を宣言します。
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
createApp(App).use(createPinia()).mount('#app')
2.useTodoListStore.js
useTodoListStore.jsではdefineStoreによってPiniaのstoreの定義を行います。
state,getters,actionsの定義を行っていますが、Piniaではmutationsが不要であることが大きな特徴となっています。
import { defineStore } from 'pinia'
export const useTodoListStore = defineStore('todoList', {
state: () => ({
todoList: [],
id: 0,
}),
getters: {
doubleCount: (state) => state.todoList.length*2
},
actions: {
addTodo(item) {
this.todoList.push({ item, id: this.id++, completed: false })
},
deleteTodo(itemID) {
this.todoList = this.todoList.filter((object) => {
return object.id !== itemID
})
},
toggleCompleted(idToFind) {
const todo = this.todoList.find((obj) => obj.id === idToFind)
if (todo) {
todo.completed = !todo.completed
}
},
},
})
3.App.vue
App.vueは2つのコンポーネントのための枠組みを与えているだけです。
<template>
<div>
<TodoList />
<TodoForm />
</div>
</template>
<script>
import TodoList from "@/components/TodoList";
import TodoForm from "@/components/TodoForm";
export default {
name: "TodoApp",
components: {
TodoList, TodoForm
},
};
</script>
4.TodoList.vue
TodoList.vueは現在のtodoリストのリスト表示を行います。
<template>
<div>
<div v-for="todo in todoList" :key="todo.id">
<div>
<span :class="{ completed: todo.completed }">{{ todo.item }}</span>
<span @click.stop="toggleCompleted(todo.id)">✔</span>
<span @click="deleteTodo(todo.id)">❌</span>
</div>
</div>
<div>
<span>2重カウント: {{ doubleCount }}</span>
</div>
</div>
</template>
<script>
import { useTodoListStore } from "@/store/useTodoListStore";
import { storeToRefs } from "pinia";
export default {
setup() {
const store = useTodoListStore();
const { todoList, doubleCount } = storeToRefs(store);
const { toggleCompleted, deleteTodo } = store;
return { todoList, doubleCount, toggleCompleted, deleteTodo };
},
};
</script>
<style>
.completed {
text-decoration: line-through;
}
</style>
useTodoListStoreでstoreにアクセスします。
import { useTodoListStore } from "@/store/useTodoListStore";
---
setup() {
---
const store = useTodoListStore();
リアクティブな値を得るためにstoreToRefsを使うことに注意してください。ここではstateのtodoListとgettersのdoubleCountを取得しています。
const { todoList, doubleCount } = storeToRefs(store);
actionsの関数はリアクティブではないので、storeからそのまま取得しています。
const { toggleCompleted, deleteTodo } = store;
5.TodoForm.vue
TodoForm.vueはtodoを追加するためのフォームです。
<template>
<form @submit.prevent="addItemAndClear(todo)">
<input v-model="todo" type="text" /><button>Add</button>
</form>
</template>
<script>
import { useTodoListStore } from '@/store/useTodoListStore'
import { ref } from "vue";
export default {
setup() {
const todo = ref('')
const store = useTodoListStore()
function addItemAndClear(item) {
if (item.length === 0) {
return
}
store.addTodo(item)
todo.value = ''
}
return { todo, addItemAndClear }
},
}
</script>
useTodoListStoreでstoreにアクセスします。
import { useTodoListStore } from '@/store/useTodoListStore'
---
setup() {
---
const store = useTodoListStore()
特にCompositionAPIの特徴ですが、setup関数内でrefを使ってリアクティブな変数todoを宣言します。todoはこのコンポーネントで使われる変数です。またsetup関数内で関数addItemAndClear()を定義して利用できるようにしています。関数addItemAndClear()内ではstore.addTodo()で直接actions関数を呼んでいます。
import { ref } from "vue";
---
setup() {
---
const todo = ref('')
---
function addItemAndClear(item) {
if (item.length === 0) {
return
}
store.addTodo(item)
todo.value = ''
}