Vue.jsとDockerで作るフロントエンド　Vue.jsを最速で理解する

# はじめに
本記事はVue.jsでフロントエンドを開発する方法について記載しています。

[**Vue.js**](https://ja.wikipedia.org/wiki/Vue.js)は、Webアプリケーションのユーザーインターフェースを構築するためのJavaScript Frameworkです。

リアクティブなデータバインディング、ルーティング、状態管理などの機能を活用してSPAの構築を容易にします。

また、Webpack、Babel、ESLintなどのツールとシームレスに統合可能とし、開発の生産性を向上する仕組みが用意されています。

https://github.com/vuejs/

## Vue.jsを使用する理由
なぜVue.jsを使用するかについて、Vue.jsの主な特徴について以下に記載しています。

### シンプル
直感的なAPI操作で開発できるため、ReactやAngularなどの他のフレームワークと比較して、文法が読みやすいです。

### パフォーマンス
Vue.jsはReactと同様に仮想DOMを採用しています。
従って実際のDOMを直接操作せずに、仮想DOMを介して変更を行うことで、ブラウザのレンダリング処理を最適化しています。

また、レンダリング処理の最適化の仕組みを行うために、リアクティブシステムを活用し、データの変更を自動的に反映します。

### コンポーネントベース
コンポーネント機能によってUIを再利用可能なパーツに分割し、保守性に優れたアプリケーションの開発が可能です。

## Vue.js開発
Vue.jsの最新のメジャーバージョンは、本記事執筆時点で**Vue 3**です。

Vue 2のサポートについては、2023年12月31日に終了が予定されています。
よくある質問の[Vue 2 と Vue 3 の違いは何ですか？](https://ja.vuejs.org/about/faq.html#what-s-the-difference-between-vue-2-and-vue-3)から大まかな違いについて確認できます。
基本的に後方互換性があるものの、Vue 3 移行ガイドの[Breaking Changes](https://v3-migration.vuejs.org/breaking-changes/)を踏まえて、重大な変更がリストされています。

本記事では、Vue.js のバージョンは2で作成し、以下のプラグインを有効にしてます。

- Babel
- Router
- Vuex
- Linter / Formatter

Vue.jsは、**Options API**と、**Composition API**の2種類の書き方があります。
詳細は公式ドキュメント[2 つの API スタイル](https://ja.vuejs.org/guide/introduction.html#api-styles)をご参照ください。

::: note warn
本記事では便宜上、Vue.jsのバージョンは2で作成しています。
後述するVuetifyは、現在Vue 3に対応しているのでこれから新規に開発する場合は、Vue 3で作成することをお勧めします。
:::

### チュートリアル
公式の[チュートリアル](https://ja.vuejs.org/tutorial/)は、環境構築不要ですぐに試すことができます。

また、Microsoftが提供しているトレーニングから[Vue.js の最初の手順](https://learn.microsoft.com/ja-jp/training/paths/vue-first-steps/)もお勧めです。

### Dockerを使用したフロントエンド開発
Dockerを使用してフロントエンド開発を行う例について以下に記載します。

ソースをマウントしている状態になるため、コードの変更は自動的に行われることで、柔軟に開発を行うことができます。

また、本記事では以下のフレームワーク及びライブラリを導入します。

- Vuetify
- vue-good-table
- axios
- Vuelidate

#### 環境構築
ソースを格納しているカレントディレクトリ（.）を起点にした、本記事のサンプルコードは以下の構成です。

```text
.
├── frontend
│   ├── Dockerfile
│   └── app
└── docker-compose.yaml
```

- docker-compose.yaml

```docker
version: '3'

services:
  vue-app:
    build:
      context: "./frontend"
      dockerfile: "Dockerfile"
    ports:
      - 8080:8080
    volumes:
      - ./frontend/app:/src
    tty: true
    environment:
      - NODE_ENV=development
```

- Dockerfile

```dockerfile
FROM node:18.12.0-buster

ENV TZ Asia/Tokyo

WORKDIR /src

RUN yarn global add @vue/cli
```

:::note
FROMで指定している`node:18.12.0-buster`は、Debian 10を基にしたイメージです。
またdockerhubに格納されているnodeのイメージは[Official Imagesのnode](https://hub.docker.com/_/node)から確認できます。名前に`slim`が付いているイメージは軽量です。
:::


上記コードを配備後、以下のコマンドを実行してデプロイします。

`$ docker-compose up -d`

<details><summary>出力例</summary>

```console
Docker Compose is now in the Docker CLI, try `docker compose up`

Building vue-app
[+] Building 86.1s (8/8) FINISHED                                                                                                     
 => [internal] load build definition from Dockerfile                                                                             0.1s
 => => transferring dockerfile: 130B                                                                                             0.0s
 => [internal] load .dockerignore                                                                                                0.0s
 => => transferring context: 2B                                                                                                  0.0s
 => [internal] load metadata for docker.io/library/node:18.12.0-buster                                                           3.4s
 => [auth] library/node:pull token for registry-1.docker.io                                                                      0.0s
 => [1/3] FROM docker.io/library/node:18.12.0-buster@sha256:e1432b8cf918f690816d66fcc4f2f687a8d19f7327006b93f17c2f5562c27761    27.6s
 => => resolve docker.io/library/node:18.12.0-buster@sha256:e1432b8cf918f690816d66fcc4f2f687a8d19f7327006b93f17c2f5562c27761     0.0s
 => => sha256:79b2348c51724c2fbd4265526abf4a048e91f8e2fb77909b067526281003b973 2.21kB / 2.21kB                                   0.0s
 => => sha256:fd31ec14dccdcda8f2472547f1a94ef0a23b8dabb764cd7b1d48aeee0afea7c8 50.45MB / 50.45MB                                 3.6s
 => => sha256:1b6422bfa32650e77d346bbb40c6b2a0dc34c863e10cfb1a117e8da9796694cd 7.86MB / 7.86MB                                   1.6s
 => => sha256:e1432b8cf918f690816d66fcc4f2f687a8d19f7327006b93f17c2f5562c27761 776B / 776B                                       0.0s
 => => sha256:9087e687d2a0976cfaef646af129bb582cc1957e1f0e2f2361adc4759a62fdb8 7.52kB / 7.52kB                                   0.0s
 => => sha256:335cbfc794fe106b207828e6c9d76fb72aa4631aad91b744e23442082275b8af 10.00MB / 10.00MB                                 1.8s
 => => sha256:2a348ba61fbef6dec66be0354c013afc02f0bba15b891b9625ac45bb007e9726 51.84MB / 51.84MB                                 4.4s
 => => sha256:f1ac0452e7a7ce5a9781502e1bca3360740aa17c776748eb301c26ca8f5b294a 192.51MB / 192.51MB                              11.5s
 => => sha256:7a53749a183a965085e17a671ec23dd3531d7db1f33675510464db37004814f3 4.20kB / 4.20kB                                   3.9s
 => => sha256:b434b0ee04a518edc0829d6dec9b1f49ddbeea75c527f7cb576d3ce4f1567710 44.65MB / 44.65MB                                 6.1s
 => => sha256:4042bd817530b7e52881ffc74407bcb3630517449726461eb318366bf862f4cb 2.28MB / 2.28MB                                   5.1s
 => => extracting sha256:fd31ec14dccdcda8f2472547f1a94ef0a23b8dabb764cd7b1d48aeee0afea7c8                                        4.1s
 => => sha256:6d5d9edc827c36a0c3a368899a3c088b98ff482ecd8d102c10922513c2aab42e 454B / 454B                                       5.5s
 => => extracting sha256:1b6422bfa32650e77d346bbb40c6b2a0dc34c863e10cfb1a117e8da9796694cd                                        0.5s
 => => extracting sha256:335cbfc794fe106b207828e6c9d76fb72aa4631aad91b744e23442082275b8af                                        0.6s
 => => extracting sha256:2a348ba61fbef6dec66be0354c013afc02f0bba15b891b9625ac45bb007e9726                                        3.3s
 => => extracting sha256:f1ac0452e7a7ce5a9781502e1bca3360740aa17c776748eb301c26ca8f5b294a                                        8.2s
 => => extracting sha256:7a53749a183a965085e17a671ec23dd3531d7db1f33675510464db37004814f3                                        0.1s
 => => extracting sha256:b434b0ee04a518edc0829d6dec9b1f49ddbeea75c527f7cb576d3ce4f1567710                                        2.5s
 => => extracting sha256:4042bd817530b7e52881ffc74407bcb3630517449726461eb318366bf862f4cb                                        0.2s
 => => extracting sha256:6d5d9edc827c36a0c3a368899a3c088b98ff482ecd8d102c10922513c2aab42e                                        0.0s
 => [2/3] WORKDIR /src                                                                                                           0.2s
 => [3/3] RUN yarn global add @vue/cli                                                                                          45.1s
 => exporting to image                                                                                                           9.4s
 => => exporting layers                                                                                                          9.4s
 => => writing image sha256:f52afd282f8fbca30519461e62c0208f51f34613009a091084ab487dd05d8fcb                                     0.0s 
 => => naming to docker.io/library/desktop_vue-app                                                                               0.0s
WARNING: Image for service vue-app was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
Creating desktop_vue-app_1 ... done
```
</details>


:::note warn
コンテナのビルド時に、[Vue CLI](https://cli.vuejs.org/)（※現在はメンテナンスモード）をインストールしています。
Vue CLIは、Vue.jsのプロジェクトを作成し、開発環境をセットアップするためのツールです。
Vue.jsのプロジェクトを生成するためのテンプレート提供や、CLI プラグインを用いて効率的な開発を実現します。
:::

#### プロジェクトの作成
Vue.jsなどのフレームワークでは、プロジェクトを作成することで、Webアプリケーションを構築するために必要なファイルやライブラリのセットを含むプロジェクトを作成します。

プロジェクトを作成するためには、コンテナにログイン後、以下のコマンドを実行します。

`$ vue create .`

プロジェクト作成時の出力例について以下に記載します。

<details><summary>出力例
</summary>

```console
Vue CLI v5.0.8
? Generate project in current directory? Yes

? Please pick a preset: 
  Default ([Vue 3] babel, eslint) 
  Default ([Vue 2] babel, eslint) 
❯ Manually select features 

? Check the features needed for your project: (Press <space> to select, <a> to toggle all, <i> to invert selection, and <enter> to proceed)
❯◉ Babel
 ◯ TypeScript
 ◯ Progressive Web App (PWA) Support
 ◉ Router
 ◉ Vuex
 ◯ CSS Pre-processors
 ◉ Linter / Formatter
 ◯ Unit Testing
 ◯ E2E Testing

? Choose a version of Vue.js that you want to start the project with 
  3.x 
❯ 2.x 

? Use history mode for router? (Requires proper server setup for index fallback in production) Yes

? Pick a linter / formatter config: 
  ESLint with error prevention only 
  ESLint + Airbnb config 
❯ ESLint + Standard config 
  ESLint + Prettier 

? Pick additional lint features: (Press <space> to select, <a> to toggle all, <i> to invert selection, and <enter> to proceed)
❯◉ Lint on save
 ◯ Lint and fix on commit

? Where do you prefer placing config for Babel, ESLint, etc.? (Use arrow keys)
❯ In dedicated config files 
  In package.json 

? Save this as a preset for future projects? (y/N) N

? Pick the package manager to use when installing dependencies: (Use arrow keys)
❯ Use Yarn 
  Use NPM 
```
</details>

プロジェクト作成時の各質問の説明は以下の通りです。

- `Generate project in current directory? Yes:`
    - 現在のディレクトリにプロジェクトを生成するかどうかの質問で、`Yes`を選択
- `Please pick a preset:`
    - プリセットを選択するかどうかの質問で、`Manually select features`を選択
- `Check the features needed for your project:`
    - プロジェクトに必要な機能を選択する質問で、`Babel、Router、Vuex、Linter / Formatter`を選択
- `Choose a version of Vue.js that you want to start the project with:`
    - Vue.js のバージョンを選択する質問で、`2.x`を選択
- `Use history mode for router? Yes:`
    - routerでhistory モードを使用するかどうかの質問で、`Yes`を選択
- `Pick a linter / formatter config:`
    - Linter / Formatterの設定を選択する質問で、`ESLint + Standard config`を選択
- `Pick additional lint features:`
    - 追加の Lint 機能を選択する質問で、`Lint on save`を選択
- `Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files:`
    - Babel、ESLint などの設定ファイルをどこに置くかを尋ねる質問で、`In dedicated config files`を選択
- `Save this as a preset for future projects? N:`
    - 今回の設定を将来のプロジェクトで使用するためのプリセットとして保存するかどうかを尋ねる質問で、`N`を選択
- `Pick the package manager to use when installing dependencies:`
    - 使用するパッケージマネージャーを選択する質問で、`Yarn`を選択

全ての質問に回答後、インストールが始まります。

``` console
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, Router, Vuex, Linter
? Choose a version of Vue.js that you want to start the project with 2.x
? Use history mode for router? (Requires proper server setup for index fallback in production) Yes
? Pick a linter / formatter config: Standard
? Pick additional lint features: Lint on save
? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? No
? Pick the package manager to use when installing dependencies: Yarn

Vue CLI v5.0.8
✨  Creating project in /src.
🗃  Initializing git repository...
⚙️  Installing CLI plugins. This might take a while...

yarn install v1.22.19
info No lockfile found.
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...

success Saved lockfile.
Done in 123.79s.
🚀  Invoking generators...
📦  Installing additional dependencies...

yarn install v1.22.19
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...

success Saved lockfile.
Done in 54.25s.
⚓  Running completion hooks...

📄  Generating README.md...

🎉  Successfully created project src.
👉  Get started with the following commands:

 $ yarn serve

 WARN  Skipped git commit due to missing username and email in git config, or failed to sign commit.
       You will need to perform the initial commit yourself.
```

以下のコマンドを実行して、ビルドします。

`$ yarn serve`

```console
yarn run v1.22.19
$ vue-cli-service serve
 INFO  Starting development server...


 DONE  Compiled successfully in 14381ms                                                                                                                                                  6:28:22 AM


  App running at:
  - Local:   http://localhost:8080/ 
  - Network: http://172.25.0.3:8080/

  Note that the development build is not optimized.
  To create a production build, run yarn build.
```

ブラウザを起動し、`http://localhost:8080`にアクセスすると、以下のような画面が表示されます。

![スクリーンショット 2022-11-10 15.29.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/bc0315dd-26dd-3a8b-a6dd-d8223f9458a5.png)

::: note
アプリケーションの変更は監視されているため、変更があった場合は自動的に再ビルドされます。これにより、開発中のアプリケーションを常に最新の状態で表示できます。
:::

#### Vuetifyの導入
[**Vuetify**](https://vuetifyjs.com/en/)は、Vue Component Frameworkです。

https://github.com/vuetifyjs/vuetify

Vuetifyには、さまざまな種類のUIコンポーネントが含まれています。
Googleをモチーフとしたマテリアルデザインに基づいたデザインシステムを採用しているので、美しく洗練されたUIを作成することができます。

以下のコマンドを実行して、Vuetifyを追加します。

`$ vue add vuetify`

本記事では**Vuetify 2**を選択しています。

```console
 WARN  There are uncommitted changes in the current repository, it's recommended to commit or stash them first.
? Still proceed? Yes

📦  Installing vue-cli-plugin-vuetify...

yarn add v1.22.19
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...

success Saved lockfile.
success Saved 4 new dependencies.
info Direct dependencies
└─ vue-cli-plugin-vuetify@2.5.8
info All dependencies
├─ null-loader@4.0.1
├─ rechoir@0.6.2
├─ shelljs@0.8.5
└─ vue-cli-plugin-vuetify@2.5.8
Done in 31.05s.
✔  Successfully installed plugin: vue-cli-plugin-vuetify

? Choose a preset: (Use arrow keys)
  Vuetify 2 - Configure Vue CLI (advanced) 
❯ Vuetify 2 - Vue CLI (recommended) 
  Vuetify 2 - Prototype (rapid development) 
  Vuetify 3 - Vite (preview) 
  Vuetify 3 - Vue CLI (preview) 
```

インストールが始まります。

```
? Choose a preset: Vuetify 2 - Vue CLI (recommended)

🚀  Invoking generator for vue-cli-plugin-vuetify...
📦  Installing additional dependencies...

yarn install v1.22.19
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...

success Saved lockfile.
Done in 31.24s.
⠙  Running completion hooks...
/src/src/views/HomeView.vue
  9:9  error  Component name "Home" should always be multi-word  vue/multi-word-component-names

✖ 1 problem (1 error, 0 warnings)
```

ブラウザを起動し、`http://localhost:8080`にアクセスすると、以下のような画面が表示されます。

![スクリーンショット 2022-11-10 16.09.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/7b961dfb-32eb-bc58-100f-a0afac259003.png)

:::note
公式ドキュメント[Icon Fonts](https://vuetifyjs.com/en/features/icon-fonts/)のとおりに一般的な4種類のアイコンがサポートされています。
Material Designのアイコンは、[Material Design Icons](https://materialdesignicons.com/)から検索できます。
:::

#### vue-good-tableの導入
[**vue-good-table**](https://xaksis.github.io/vue-good-table/)は、Vue.jsで使用可能な表（テーブル）をカスタムするためのプラグインです。

https://github.com/xaksis/vue-good-table

様々なデータ形式に対応しています。
テーブルに対して、ソート、フィルタリング、ページネーション、行選択、グループ化、集計などの機能があります。
また、カスタムスロットを使用して、テーブルの見出し、フッター、カラム、セルに独自のコンテンツを挿入することができます。

以下のコマンドを実行してvue-good-tableをインストールします。
オプションに`--legacy-peer-deps`を付与することで依存関係を考慮しています。

`$ npm install --save --legacy-peer-deps vue-good-table`

```console
added 3 packages, removed 1 package, changed 2 packages, and audited 954 packages in 41s

136 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
npm notice 
npm notice New major version of npm available! 8.19.2 -> 9.1.1
npm notice Changelog: https://github.com/npm/cli/releases/tag/v9.1.1
npm notice Run npm install -g npm@9.1.1 to update!
npm notice 
```

#### axiosの導入
[**axios**](https://axios-http.com/)は、ブラウザー及びnode.js用のPromiseベースのHTTPクライアントです。

https://github.com/axios/axios

axiosを使用することで、HTTPリクエストの送信や、HTTPレスポンスの受信を行うことができます。
また、PromiseベースのAPIを提供するため、非同期処理を実現することができます。

Vue.jsと組み合わせて使用する場合、Vue.jsのコンポーネント内でデータを取得するために使用されることが一般的です。

以下のコマンドを実行して追加します。

`$ yarn add axios`

```console
yarn add v1.22.19
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
warning " > @vue/eslint-config-standard@6.1.0" has incorrect peer dependency "eslint-plugin-vue@^7.0.0".
warning "@vue/eslint-config-standard > eslint-import-resolver-webpack@0.13.2" has unmet peer dependency "webpack@>=1.11.0".
warning " > sass-loader@10.3.1" has unmet peer dependency "webpack@^4.36.0 || ^5.0.0".
warning " > vue-cli-plugin-vuetify@2.5.8" has unmet peer dependency "webpack@^4.0.0 || ^5.0.0".
warning "vue-cli-plugin-vuetify > null-loader@4.0.1" has unmet peer dependency "webpack@^4.0.0 || ^5.0.0".
warning " > vuetify-loader@1.9.2" has unmet peer dependency "webpack@^4.0.0 || ^5.0.0".
warning "vuetify-loader > file-loader@6.2.0" has unmet peer dependency "webpack@^4.0.0 || ^5.0.0".
[4/4] Building fresh packages...

success Saved lockfile.
success Saved 6 new dependencies.
info Direct dependencies
└─ axios@1.1.3
info All dependencies
├─ asynckit@0.4.0
├─ axios@1.1.3
├─ combined-stream@1.0.8
├─ delayed-stream@1.0.0
├─ form-data@4.0.0
└─ proxy-from-env@1.1.0
Done in 13.94s.
```

#### Vuelidateの導入
[Vuelidate](https://vuelidate.js.org/)は、Vue.jsアプリケーションでフォームバリデーションを実装するためのライブラリです。

https://github.com/vuelidate/vuelidate

バリデーションルールを定義し、コンポーネントのデータモデルに関連付けることで、入力値のバリデーションを行います。リアクティブな性質を活用し、データが変更されるたびに自動的にバリデーションを行うため、バリデーションの実装が簡単になります。

以下のコマンドを実行して、Vuelidateを追加します。

`$ npm install --save --legacy-peer-deps vuelidate`

```console
added 1 package, and audited 960 packages in 3s

136 packages are looking for funding
  run `npm fund` for details

1 critical severity vulnerability

To address all issues, run:
  npm audit fix

Run `npm audit` for details.
```

# おわりに
Vue.jsのコードネームについて「One Piece」や、「Evangelion」など日本のアニメ作品の名前が付けられているのは、創始者及び開発者である尤雨溪氏がアニメ好きなところからきているようです。

## 参考
### Vue.js
- [The Progressive JavaScript Framework](https://ja.vuejs.org/)