【TypeScript + Vue3 + Vite + Quasar】雛形プロジェクト作成

# はじめに

こんにちは！
今回は、雛形プロジェクト作成について記載します。


## 実行環境

|     | バージョン・環境等 |
| :-- | :--: |
| PC | MacBook Air (InterlCore i7) |
| MacOS | Ventura |
| Node.js | v18.18.0 |
| npm | 10.2.0 |
| Vite | 4.5.0 |
| Vue | 3.3.8 |
| Quasar | 2.13.1 | 

## 構築手順

### 1. ViteでVueプロジェクトの作成

まずはViteの公式ドキュメントに記載のようにプロジェクトを作成する。

https://ja.vitejs.dev/guide/#%E6%9C%80%E5%88%9D%E3%81%AE-vite-%E3%83%95%E3%82%9A%E3%83%AD%E3%82%B7%E3%82%99%E3%82%A7%E3%82%AF%E3%83%88%E3%82%92%E7%94%9F%E6%88%90%E3%81%99%E3%82%8B

```
$ npm create vite@latest
or
$ yarn create vite
```

※ 本記事では yarn を利用します。

yarn の実行後↓

```
yarn create v1.22.17
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 🔨  Building fresh packages...
success Installed "create-vite@4.4.1" with binaries:
      - create-vite
      - cva
✔ Project name: … bimitan
✔ Select a framework: › Vue
✔ Select a variant: › TypeScript

Scaffolding project in /Users/hoge...

Done. Now run:

  cd bimitan
  yarn
  yarn dev

✨  Done in 18.50s.
```

`yarn install` でパッケージを取り込んだ後に、 `yarn dev` をルートディレクトリで実行すると、localhost が立ち上がります。

今回は、 Vue -> TypeScript を選択しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/966101/5a571cb0-0482-23df-7a54-001fe31a2ab4.png)


### 2. npm-check-updates のインストール

package.json の依存関係をコマンドひとつで最新バージョンにアップグレードできるツールを導入。

https://github.com/raineorshine/npm-check-updates

```
$ yarn global add npm-check-updates
```

`ncu` コマンドで最新のバージョンと差分がないかを確認し、

```
$ ncu
Checking /Users/hoge/package.json
[====================] 5/5 100%

 @vitejs/plugin-vue  ^4.2.3  →   ^4.4.1
 typescript          ^5.0.2  →   ^5.2.2
 vite                ^4.4.5  →   ^4.5.0
 vue                 ^3.3.4  →   ^3.3.8
 vue-tsc             ^1.8.5  →  ^1.8.22
```

 `ncu -u` コマンドでパッケージのアップグレードを一括で行います。

```
$ ncu -u
Upgrading /Users/hoge/package.json
[====================] 5/5 100%

 @vitejs/plugin-vue  ^4.2.3  →   ^4.4.1
 typescript          ^5.0.2  →   ^5.2.2
 vite                ^4.4.5  →   ^4.5.0
 vue                 ^3.3.4  →   ^3.3.8
 vue-tsc             ^1.8.5  →  ^1.8.22

Run npm install to install new versions.
```

### 3. Quasar の導入

公式ドキュメントに記載の通りコマンドを実行してみる。

https://quasar.dev/start/vite-plugin#installation

```
$ yarn add quasar @quasar/extras
$ yarn add --dev @quasar/vite-plugin sass@1.32.12
```

:::note warn
VueCLIでプロジェクトを作成した場合と、Viteで作成した場合で導入手順が異なるようです。

VueCLIでプロジェクトを作成した方は、下記のページのコマンドを実行して下さい。
[Vue CLI Quasar プラグイン](https://quasar.dev/start/vue-cli-plugin)
:::

上記コマンドを実行後、公式ドキュメントに記載のように、下記３ファイルを修正する。

- main.ts
- vite.config.ts
- src/quasar-variables.sass

```ts: main.ts
import { createApp } from 'vue'
import { Quasar } from 'quasar'

// Import icon libraries
import '@quasar/extras/material-icons/material-icons.css'

// Import Quasar css
import 'quasar/src/css/index.sass'

// Assumes your root component is App.vue
// and placed in same folder as main.js
import App from './App.vue'

const myApp = createApp(App)

myApp.use(Quasar, {
  plugins: {}, // import Quasar plugins and add here
})

// Assumes you have a <div id="app"></div> in your index.html
myApp.mount('#app')
```

```ts: vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { quasar, transformAssetUrls } from '@quasar/vite-plugin'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue({
      template: { transformAssetUrls }
    }),

    // @quasar/plugin-vite options list:
    // https://github.com/quasarframework/quasar/blob/dev/vite-plugin/index.d.ts
    quasar({
      sassVariables: 'src/quasar-variables.sass'
    })
  ]
})
```

```sass: src/quasar-variables.sass
$primary   : #1976D2
$secondary : #26A69A
$accent    : #9C27B0

$dark      : #1D1D1D

$positive  : #21BA45
$negative  : #C10015
$info      : #31CCEC
$warning   : #F2C037
```

Quasar 導入後に `yarn dev` を実行しても、エラーなく画面が表示されるとを確認できました。

### 4. VSCode プラグインの導入

Vue3で開発するため、下記のプラグインを導入します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/966101/c679bbb4-efd4-8584-3d47-42afd55d14d7.png)

- [Vue Language Features (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.volar)
    - Vue、Vitepress、および petite-vue 用に構築された言語サポート拡張機能です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/966101/8999bb1b-298a-dcd3-7084-a0e99756a5df.png)

- [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin)
    - TS サーバーに *.vue ファイルを認識させるための TS サーバープラグイン。

### 5. 拡張機能の追加

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/966101/19f78306-6259-d1e6-3ed2-207f274fccb1.png)

https://blog.serverworks.co.jp/setup-vite-vue-typescript



### 6. 簡単なレイアウトの作成

Quasar を利用して、簡単なトップページを作成してみようと思います。
まずは、 `src/` ディレクトリ配下にヘッダー・フッター・トップページ用に `.vue` ファイルを作成する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/966101/187979d4-5981-078f-25a4-f15dcc069299.png)


Quasar で使えそうなヘッダーとフッターのレイアウトを検索する。
「[Layout and Grid > Layout Header and Footer](https://quasar.dev/layout/header-and-footer#usage)」にいくつかテンプレートがあるので、そちらを利用する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/966101/715654c8-809f-fb72-24e8-32ef40616d45.png)


```vue: Basic
<template>
  <div class="q-pa-md">
    <q-layout view="lHh lpr lFf" container style="height: 400px" class="shadow-2 rounded-borders">
      <q-header elevated>
        <q-toolbar>
          <q-btn flat round dense icon="menu" class="q-mr-sm" />
          <q-avatar>
            <img src="https://cdn.quasar.dev/logo-v2/svg/logo-mono-white.svg">
          </q-avatar>

          <q-toolbar-title>Quasar Framework</q-toolbar-title>

          <q-btn flat round dense icon="whatshot" />
        </q-toolbar>
      </q-header>

      <q-footer elevated>
        <q-toolbar>
          <q-toolbar-title>Footer</q-toolbar-title>
        </q-toolbar>
      </q-footer>

      <q-page-container>
        <q-page class="q-pa-md">
          <p v-for="n in 15" :key="n">
            Lorem ipsum dolor sit amet consectetur adipisicing elit. Fugit nihil praesentium molestias a adipisci, dolore vitae odit, quidem consequatur optio voluptates asperiores pariatur eos numquam rerum delectus commodi perferendis voluptate?
          </p>
        </q-page>
      </q-page-container>
    </q-layout>
  </div>
</template>
```

上記のサンプルコードを見る限り、どうやらテンプレートは、header/hooter/main の順序で構成されているようなので、それらを分解して各 vue ファイルにテンプレートを配置していく。
シンプルなレイアウトにしたいので、少し修正を加える。

```vue: AppHeader.vue
<template>
  <q-header elevated>
    <q-toolbar class="glossy">
      <q-avatar>
        <img src="../../assets/images/vue.svg">
      </q-avatar>
      <q-toolbar-title>SampleApp</q-toolbar-title>
      <q-btn flat round dense icon="menu" />
    </q-toolbar>
  </q-header>
</template>
```

footer には copyright のテキストは中央寄せにしたいので、 `text-center` を class につける。

```vue: AppFooter.vue
<template>
  <q-footer elevated>
    <q-toolbar class="glossy text-center">
      <q-toolbar-title>
        <small>Copyright (c) 2023 SampleApp. All Rights Reserved.</small>
      </q-toolbar-title>
    </q-toolbar>
  </q-footer>
</template>
```

ページの main コンテンツの Top.vue には、検索フォームを配置してみる。
「[Vue Components > Form Components > Input Textfield](https://quasar.dev/vue-components/input#debouncing-model)」 の検索フォームのテンプレートを利用する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/966101/8f93d637-de00-f7e4-e1de-1587a915086d.png)

`fixed-center` でアイテムを中央寄せする。

```vue: Top.vue
<script setup lang="ts">
import { ref } from 'vue'

const search = ref('')
</script>

<template>
  <q-page-container>
    <q-page class="q-pa-md fixed-center" style="min-height: 0;">
      <h1 class="text-h4">入力してください</h1>
      <div class="q-pa-md" style="max-width: 350px">
        <div class="q-gutter-md">
          <q-input
            v-model="search"
            debounce="50"
            filled
            placeholder="Search"
          >
            <template v-slot:append>
              <q-icon name="search" />
            </template>
          </q-input>
        </div>
      </div>
    </q-page>
  </q-page-container>
</template>
```

`src/` ディレクトリ直下の App.vue に読み込む記述を追加する。

```vue: App.vue
<script setup lang="ts">
import AppHeader from './components/common/AppHeader.vue'
import AppFooter from './components/common/AppFooter.vue'
import PageTop from './pages/Top.vue'
</script>

<template>
  <div>
    <q-layout view="lHh lpr lFf" container style="height: 100vh" class="shadow-2 rounded-borders">
      <AppHeader/>
      <AppFooter/>
      <PageTop/>
    </q-layout>
  </div>
</template>
```

出来上がったデザインは下記のようになる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/966101/a0af6c3c-8d45-f69e-887f-76aec509b41e.png)

### 7. vue-router の導入

現在は App.vue に直接トップページのコンテンツを import させているが、ページごとに表示させるものを変更したいので、vue-router を導入する。

```
$ yarn add vue-router
```

追加したら、ログインページ・router の設定ファイルを作成して、既存のコードを書き換えてみる。

```vue: src/pages/Login.vue
<template>
  <q-page-container>
    <q-page class="q-pa-md fixed-center" style="min-height: 0;">
      <h1 class="text-h4">ログインページ</h1>
    </q-page>
  </q-page-container>
</template>
```

```ts: src/router.ts
import { createRouter, createWebHistory } from 'vue-router';

const routes = [
  {
    path: '/',
    name: 'top',
    component: async () => {
      const top = await import('./pages/Top.vue');
      return top;
    }
  },
  {
    path: '/login',
    name: 'login',
    component: async () => {
      const login = await import('./pages/Login.vue');
      return login;
    }
  }
];

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes
});

export default router;
```

下記ページをそれぞれ書き換える。

```vue: src/App.vue
<script setup lang="ts">
import AppHeader from './components/common/AppHeader.vue'
import AppFooter from './components/common/AppFooter.vue'
import PageTop from './pages/Top.vue' // 削除
</script>

<template>
  <q-layout view="lHh lpr lFf" container style="height: 100vh" class="shadow-2 rounded-borders">
    <AppHeader/>
    <AppFooter/>
    <PageTop/>     <!-- 削除 -->
    <RouterView /> <!-- 追加 -->
  </q-layout>
</template>
```

```ts: src/main.ts
import { createApp } from 'vue'
import { Quasar } from 'quasar'

import '@quasar/extras/material-icons/material-icons.css'
import 'quasar/src/css/index.sass'

import './style.css';
import App from './App.vue'
import router from './router' // 追加

const myApp = createApp(App)

myApp.use(Quasar, {
  plugins: {},
})
myApp.use(router) // 追加

myApp.mount('#app')
```

下記のように、ルーティングが適応されてコンテンツが書き変わっていることが確認できた。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/966101/89302e65-6741-e76b-aed8-41530dd0239a.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/966101/5bcd1d38-4390-978a-8fc6-ac674f114e71.png)

# 今回作成したリポジトリ

https://github.com/ryota-sato07/vue3-ts-quasar-sample-app


# 参考

https://qiita.com/yuta-katayama-23/items/a40775326914ee9ed89a#vuetify%E3%81%AE%E5%B0%8E%E5%85%A5express%E3%82%92vitevuevuetify%E3%81%AE%E5%90%8C%E4%B8%80pj%E3%81%A7%E9%96%8B%E7%99%BA%E3%81%A7%E3%81%8D%E3%82%8B%E3%82%88%E3%81%86%E3%81%AB%E3%81%99%E3%82%8B

https://qiita.com/satoshi256kbyte/items/23feae35fe8cd26fbad8#quasar%E3%81%AE%E9%81%A9%E7%94%A8

https://blog.serverworks.co.jp/setup-vite-vue-typescript

https://zenn.dev/satjopg/scraps/86cde0f7b5883f