Closed9
[キャッチアップ] Pinia
ちょっと整理できてまとまりそうだったら記事にするためのメモ
概要
- Pinia は Vuex 5 が目指すグローバルステートの扱いの実証実験の立ち位置
- Vuex はコミュニティ内の RFC を通じて慎重に仕様が固められていくので、その哲学をテストとして開発される
- 将来的な Vuex5 への移行や統合をしやすいように設計されている
- Vue2, Vue3 両方に対応し、SSR, CSR いずれも可能
Vuex 3,4 と比べて
- TypeScript の完全なサポート
- CodeSpliting
- Mutations の廃止 (多くの場合それは冗長なので)
- ネストされたモジュールの廃止
- ネームスペースの廃止
プロジェクト作る (Vite, Vue3, TypeScript)
$ yarn create @vitejs/app 1030_pinia --template vue-ts
$ cd 1030_pinia
$ yarn install
スキャフォルドされる Hello, World コードを適当に削除してまっさらなアプリケーションにする。
(この辺もテンプレートの設定でできる気がする)
pinia をインストール
$ yarn add pinia
今回はs最新の 2.0.0 を使用する
Vue App での pinia の使用を宣言する
src/main.ts
import { createApp } from "vue";
import { createPinia } from "pinia";
import App from "./App.vue";
createApp(App).use(createPinia()).mount("#app");
ストアを作成する
src/store/todos.ts
import { defineStore } from "pinia";
type FilterType = "all" | "finished" | "unfinished";
type TODO = {
id: number;
label: string;
finished: boolean;
};
export const useTodoStore = defineStore("todos", {
state: () => {
return {
filter: "all" as FilterType,
todos: [] as TODO[],
nextId: 0,
};
},
getters: {
findTodo(state) {
return (id: number): TODO => {
const todo = state.todos.find((todo) => todo.id === id);
if (todo === undefined) throw new Error("todo not found");
return todo;
};
},
finishedTodos(state) {
return state.todos.filter((todo) => todo.finished);
},
unfinishedTodos(state) {
return state.todos.filter((todo) => !todo.finished);
},
filteredTodos(state): TODO[] {
switch (state.filter) {
case "finished":
return this.finishedTodos;
case "unfinished":
return this.unfinishedTodos;
default:
return this.todos;
}
},
},
actions: {
addTodo(label: string) {
this.todos.push({ id: this.nextId++, label, finished: false });
},
toggleTodo(id: number) {
const todo = this.findTodo(id);
todo.finished = !todo.finished;
},
},
});
コンポーネント側
App.vue
<template>
<input v-model="state.newTodoLabel" />
<button @click="addTodo">add</button>
<input id="all" type="radio" v-model="filter" value="all" />
<label for="all">すべて</label>
<input id="finished" type="radio" v-model="filter" value="finished" />
<label for="finished">完了済み</label>
<input id="unfinished" type="radio" v-model="filter" value="unfinished" />
<label for="unfinished">未完了</label>
<ul>
<li
:class="{ todo: true, finished: todo.finished }"
:key="todo.label"
v-for="todo in filteredTodos"
v-text="todo.label"
@click="toggleTodo(todo.id)"
/>
</ul>
</template>
<script setup lang="ts">
import { storeToRefs } from "pinia";
import { reactive } from "vue";
import { useTodoStore } from "./store/todos";
const state = reactive({ newTodoLabel: "" });
const store = useTodoStore();
const { filteredTodos, filter } = storeToRefs(store);
const toggleTodo = (id: number) => store.toggleTodo(id);
const addTodo = () => {
if (state.newTodoLabel !== "") {
store.addTodo(state.newTodoLabel);
state.newTodoLabel = "";
}
};
</script>
<style scoped>
.todo {
user-select: none;
cursor: pointer;
}
.todo.finished {
text-decoration: line-through;
color: gray;
}
</style>
このスクラップは2021/12/09にクローズされました