[Vue3 Vite] Vue3+TypeScript+Vuetify3+Piniaのベースつくってみた

この記事ではベースを作るまでの流れについて書いていきます。コードは下記からどうぞ
https://github.com/daiki-gaasuu/vue3-typecript-vuetify3-pinia-vite
## 前提条件
- node→v14.18.0 || >=16.0.0
- Vue CLI 5.0 以上

## 1. Viteでプロジェクト作成
1-1. `yarn create vite`でプロジェクト作成
1-2. プロジェクト名入力
1-3. Select a framework→Vue
1-4. Select a variant→TypeScript
1-5. プロジェクトルートで`yarn`→`yarn dev`で起動


## 2. Vuetify追加
2-1. プロジェクトルートで`vue add vuetify`
2-2. Choose a preset→Vuetify 3 - Vite (preview)

## 3. Vue-Router追加
3-1. `yarn add vue-router`
3-2. src/views/にHome.vue作成
```vuejs:Home.vue
<template>
  <HelloWorld />
</template>
<script lang="ts" setup>
  import HelloWorld from "../components/HelloWorld.vue"
</script>
```
3-3. src/router/index.tsを作成
```javascript:index.ts
import { createRouter, createWebHistory } from "vue-router";

const routes = [
  {
    path: "/",
    name: "Home",
    component: () => import("../views/Home.vue"),
  },
];

const router = createRouter({
  history: createWebHistory(),
  routes,
});
export default router;

```
3-4. main.tsにrouter追加
```javascript:main.ts
import { createApp } from 'vue'
import App from './App.vue'
import vuetify from './plugins/vuetify'
import router from "./router" // ←追加
import { loadFonts } from './plugins/webfontloader'

loadFonts()

createApp(App)
  .use(vuetify) // ←追加
  .use(router)
  .mount('#app')
```

3-5. App.vueの変更
```vuejs:App.vue
<template>
  <v-app>
    <v-main>
      <router-view />
    </v-main>
  </v-app>
</template>

<script setup lang="ts">
</script>
```

これでルートにアクセスして問題なく表示されればvue-routerの導入は終わりです。

## 4. Pinia追加しサンプルコード記述
4-1. `yarn add pinia`
4-2. main.tsにpinia追加
```javascript:main.ts
import { createApp } from 'vue'
import {createPinia } from "pinia" //←追加
import App from './App.vue'
import vuetify from './plugins/vuetify'
import router from "./router"
import { loadFonts } from './plugins/webfontloader'

loadFonts()

createApp(App)
  .use(createPinia()) //←追加
  .use(vuetify)
  .use(router)
  .mount('#app')
```
4-3. src/store/counter.ts作成
```javascript:counter.ts
import {defineStore} from "pinia";
import { ref } from "vue";

// defineStore 関数を用いてストアを作成する
// 第一引数 "counter" はアプリケーション全体でストアを特定するためのユニークキー
export const useCounterStore = defineStore("counter",() => {
  const count = ref(0);

  const doubleCount = () => {
    return count.value * 2;
  };

  const increment = () => {
    count.value++;
  };

  return {
    count,
    doubleCount,
    increment
  }

})
```
4-4. 作成したcounterをHelloWorld.vueで使えるようにする
```vuejs:HelloWorld.vue
<template>
  <v-container>
    <p>counter: {{ counter.count }}, doubleCounter: {{ counter.doubleCount() }}</p>
    <v-btn @click="counter.increment">+1</v-btn>
  </v-container>
</template>

<script setup lang="ts">
import { useCounterStore } from "../store/counter";
const counter = useCounterStore();
</script>
```
これでストアとしてPiniaが使えるようになりました。次はPiniaのデータの永続化をしていきます。

## 5. Piniaのデータ永続化
ストアの値は何もしないとページ遷移やリロードなどのイベントで初期化されてしまいます。
Piniaを知る前はVuexを使用していたのですが、その時は「vuex-persistedstate」という便利なライブラリがあり簡単に永続化できました。
Piniaにも同じようなものがないかと調べたところ・・・ありました。
https://github.com/iendeavor/pinia-plugin-persistedstate-2

5-1. `yarn add pinia-plugin-persistedstate-2`
5-2. src/store/index.tsを作成
```javascript:index.ts
import { Plugin } from "vue";
import { createPinia } from "pinia";
import { createPersistedStatePlugin } from "pinia-plugin-persistedstate-2";

export const plugin: Plugin = (app) => {
  const pinia = createPinia();

  const installPersistedStatePlugin = createPersistedStatePlugin();
  pinia.use((context) => installPersistedStatePlugin(context));

  app.use(pinia);
};
```
5-3. main.tsを書き換え
```javascript:main.ts
import { createApp } from "vue";
import App from "./App.vue";
import { plugin as storePlugin } from "./store";
import vuetify from "./plugins/vuetify";
import router from "./router";
import { loadFonts } from "./plugins/webfontloader";

loadFonts();

createApp(App).use(storePlugin).use(vuetify).use(router).mount("#app");
```
これでページ遷移やリロードをしてもストアのデータが保持されるようになりました。

## 最後に
Viteを初めて使ったのですが、起動した時にVueCLIとは比べものにならないほど起動が速くて驚きました。また、VuexからPiniaにしたことでコードの記述量もかなりコンパクトになりましたし、これからはこのベースをメインに色々作っていこうと思います。