Quasar + Vite + TypeScript で始める爆速 Vue 3 開発

## はじめに

この記事では、次のような構成で Vue 3 開発を行うためのセットアップ手順について紹介します。

- 言語: TypeScript
- ビルドツール: Vite
- UI フレームワーク: Quasar

### Quasar とは

https://quasar.dev/

Vue 3 に対応した Material Design の UI フレームワークです。
「パフォーマンス重視」を謳っていることが特徴のようです。

同じく Vue 用の Material Design UI フレームワークとしては [Vuetify](https://vuetifyjs.com/) が人気ですが、Quasar もそれに負けないくらいの機能を持っておりドキュメントも充実しています (日本語のドキュメントはありません)。
また、Vuetify に比べるとかなり早く Vue 3 に対応できている[^1]点も魅力です。

[^1]: Vuetify の Vue 3 対応バージョンである [v3 (Titan)](https://vuetifyjs.com/en/introduction/roadmap/#v3-0-titan) はリリースが遅れており、執筆時点では 2022 年 5 月リリース予定となっています。

ちなみに便宜上ここでは UI フレームワークとして紹介しましたが、実際には UI コンポーネントつきの Vue フレームワークといった方が適切で、立ち位置的には Vuetify よりも [Nuxt.js](https://nuxtjs.org/) の方が近いかもしれません。
そのため単なる UI フレームワークを求めている場合、Quasar では機能が過剰で要求に合わない可能性があります。
ただしセットアップ方法次第では単なる UI フレームワークのように扱うことも可能です。今回紹介するセットアップ方法がこれに当たります。

### Vite とは

https://vitejs.dev/

Vue や React, Svelte などをサポートしている高速なビルドツールです。

Vite はソースコードを Native ESM として扱います。
そして開発時にはソースコード全体をバンドルするのではなく、ブラウザからのリクエストに応じて必要なモジュールのみを提供する仕組みとなっています。
これによりバンドル型のビルドツールと比較して開発サーバの起動や HMR が非常に高速となっています。

ソースコードを Native ESM で扱う都合上、Vite は [依存関係の事前バンドル](https://vitejs.dev/guide/dep-pre-bundling.html) を行い CommonJS や UMD を ESM に変換します。
この事前バンドルは [esbuild](https://esbuild.github.io/) によって行われるため非常に高速且つキャッシュもされるのですが、変換が必要な大きな依存パッケージがあるとやはりその分の時間はかかってしまいます。
その点 Quasar は ESM でも提供されているため変換が不要であり、高速な起動が期待できます。

## 注意

この記事では Vite で Vue プロジェクトを構築し、Quasar は Vite プラグインとして導入します。
この場合 Quasar は単なる UI フレームワークとして使用することになり、[Quasar CLI](https://quasar.dev/start/quasar-cli) による開発支援など Quasar のフル機能の恩恵を受けることはできません。

執筆時点ではこれが Quasar + Vite を実現するためのオフィシャルな方法ですが、将来的には Quasar CLI でビルドツールのオプションとして Vite を選択できるようにする計画があり、そちらが推奨されるようです。
Quasar を UI フレームワークとして使うのではなく、Quasar アプリケーションのビルドツールとして Vite を使いたい場合は公式ドキュメント等から最新の方法をチェックするようにしてください。

## 環境

この記事では次の環境を使用しています。

- Node.js v16.14.0
- npm v8.5.0

なお、以下のセットアップ手順はあくまでも執筆時のものなので、正確な手順は公式ドキュメントを参照してください。

## Vue プロジェクト作成

Vite の公式ドキュメントに従って新規 Vue プロジェクトを作成します。

https://vitejs.dev/guide/

TypeScript を使いたいので `vue-ts` テンプレートを使用します。

```console
$ npm create vite@latest my-app -- --template vue-ts
```

プロジェクトが scaffold されたら、依存パッケージのインストールを済ませて動作確認をします。
`npm run dev` で開発サーバを起動することができます。

```console
$ cd my-app
$ npm run install
$ npm run dev
```

`vite.config.ts` で Node.js の機能を使えるようにしておきましょう。

Node.js の型定義ファイルをインストールします。
使用している Node.js のメジャーバージョンに合わせてバージョン指定しています。

```console
$ npm install --save-dev @types/node@16
```

`tsconfig.node.json` で [ES Module Interop](https://www.typescriptlang.org/tsconfig#esModuleInterop) を有効にしておきます。
これにより [path](https://nodejs.org/api/path.html) などの標準モジュールを default import できるようになります。

```jsonc:tsconfig.node.json
{
  "compilerOptions": {
    "composite": true,
    "module": "esnext",
    "moduleResolution": "node",
    "esModuleInterop": true
  },
  "include": ["vite.config.ts"]
}
```

### `src/` 以下を `@` で import できるようにする (Optional)

鉄板の設定として `src/` 以下のファイルを `@` で import できるようにしてみます。

これを、

```typescript:src/App.vue
import HelloWorld from './components/HelloWorld.vue';
```

こんな風に import できるようにするための設定です。

```typescript:src/App.vue
import HelloWorld from '@/components/HelloWorld.vue';
```

`tsconfig.json` に [Base Url](https://www.typescriptlang.org/tsconfig#baseUrl) と [Paths](https://www.typescriptlang.org/tsconfig#paths) の設定を追加して、TypeScript が `@` を `src/` 以下のファイルで解決するようにします。

```jsonc:tsconfig.json
{
  "compilerOptions": {
    "target": "esnext",
    "useDefineForClassFields": true,
    "module": "esnext",
    "moduleResolution": "node",
    "strict": true,
    "jsx": "preserve",
    "sourceMap": true,
    "resolveJsonModule": true,
    "esModuleInterop": true,
    "lib": ["esnext", "dom"],
    "baseUrl": "./",
    "paths": {
      "@/*": ["src/*"]
    }
  },
  "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
  "references": [{ "path": "./tsconfig.node.json" }]
}
```

これだけだとビルド時のパス解決は行われないので、Vite 側でも [resolve.alias](https://vitejs.dev/config/#resolve-alias) を設定して、`@` を `src/` 以下のファイルで解決するように設定します。

```typescript:vite.config.ts
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import path from "path";

export default defineConfig({
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "src"),
    },
  },
  plugins: [vue()],
});
```

## Quasar 導入

Vite を使用する場合は次のドキュメントに記載された手順に従ってセットアップします。

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

[quasar](https://www.npmjs.com/package/quasar) と [@quasar/extras](https://www.npmjs.com/package/@quasar/extras) をインストールします。

[@quasar/extras](https://github.com/quasarframework/quasar/blob/dev/extras/README.md) はフォントやアイコン、アニメーションなどが入ったパッケージで、必須ではありません。
個人的に [Material Icons](https://fonts.google.com/icons?selected=Material+Icons) を使いたいので導入しています。

```console
$ npm install quasar @quasar/extras
```

[@quasar/vite-plugin](https://www.npmjs.com/package/@quasar/vite-plugin) と [sass](https://www.npmjs.com/package/sass) を `devDependencies` に追加します。

[sass](https://www.npmjs.com/package/sass) も Optional ですが、[Quasar で定義された Sass / SCSS 変数](https://quasar.dev/style/sass-scss-variables) を使用したい場合に必要となります。
細かいバージョン指定があるので注意してください。これは [dart-sass v1.33.0](https://github.com/sass/dart-sass/releases/tag/1.33.0) で [`/` による除算が非推奨になった](https://sass-lang.com/documentation/breaking-changes/slash-div) ことに由来しているものと思われますので、`1.32.x` の最新版を入れておけば OK です。

```console
$ npm install -D @quasar/vite-plugin sass@1.32.13
```

公式ドキュメントの configurator を活用して設定ファイルを生成します。

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

今回は Material Icons を有効にし、日本語の [Language Pack](https://quasar.dev/options/quasar-language-packs) を使用して Quasar コンポーネントを日本語化する設定にしてみました。

生成された `main.js` の内容を `main.ts` にコピーします。

```typescript:main.ts
import { createApp } from "vue";
import { Quasar } from "quasar";
import quasarLang from "quasar/lang/ja";

import "@quasar/extras/material-icons/material-icons.css";

import "quasar/src/css/index.sass";

import App from "./App.vue";

const myApp = createApp(App);

myApp.use(Quasar, {
  plugins: {},
  lang: quasarLang,
});

myApp.mount("#app");
```

生成された `vite.config.js` の内容を `vite.config.ts` にコピーします。
すでに `vite.config.ts` をいじっている場合はいい感じにマージします。

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

export default defineConfig({
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "src"),
    },
  },
  plugins: [
    vue({
      template: { transformAssetUrls },
    }),
    quasar({
      sassVariables: "src/quasar-variables.sass",
    }),
  ],
});
```

生成されたものをコピーして `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
```

## VSCode の設定をする (Optional)

エディタとして VSCode を設定している場合は Vue 開発用の Extension として Volar をインストールしておきましょう。

https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.volar

https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.vscode-typescript-vue-plugin

Vue 2 開発で [Vetur](https://marketplace.visualstudio.com/items?itemName=octref.vetur) を使用していた場合は Volar と競合するので無効化しておきます。

## アプリケーションを開発する

これでセットアップは完了なので実際に開発を始めることができます。

繰り返しになりますが、`npm run dev` で開発サーバを起動することができます。
Vite のスピードを体感しましょう。

```console
$ npm run dev
```

また、`npm run build` で本番ビルドが可能です。

```console
$ npm run build
```

本番ビルドの成果物は `npm run preview` で起動したローカルサーバで確認することができます。

```console
$ npm run preview
```

Quasar の使い方についての詳しい解説はしませんが、まずは Quasar 公式ドキュメントの [Layout and Grid](https://quasar.dev/layout/) や [Vue Components](https://quasar.dev/vue-components/) のあたりを眺めつつ色々と触ってみると大体わかってくると思います。

また、公式に提供されている Layout Builder もとっかかりとしておすすめです。

https://quasar.dev/layout-builder

## サンプル

今回紹介した構成で簡単な ToDo アプリを作ってみました。
ディレクトリ構成などは Quasar CLI で生成されるものに寄せています。
こちらでは [Vue Router](https://router.vuejs.org/) の導入や [ESLint](https://eslint.org/) の設定なども行っているので気になる方は参考にしてみてください。

https://github.com/frozenbonito/quasar-vite-ts