DockerでNode.jsを使ってVue.js勉強環境を作って公式チュートリアルをやってみる

# はじめに
普段はバックエンドでPHPを使って仕事をしています。CakePHP歴1年、Laravel歴1ヶ月ほど。仕事ではほんの少しのJavaScript、JQueryをメンテナンスする、というほどのJavaScript歴です。

会社のとあるプロジェクトのバックエンド側で、VueでやるのかReactでやるのか、派閥が軽く分かれて争いが始まりそう。私はReactもVueも全くわからないですが、それぞれがどんなものか触っていこうと思います。

最小限のDockerfile、docker-compose.yamlでNode.jsを使える環境を作り、Vue.jsを公式チュートリアルに沿って学習するまでの流れです。

MacでVSCodeを使っています。

# Vue.jsを学ぶ環境の選択

https://zenn.dev/comm_vue_nuxt/articles/ways-of-vuejs-setup-app

こちらの記事にもいろいろな環境が紹介されていますが、とにかくシンプルにいきたい。調べた結果、DockerでNode.jsを使うのが一番簡単にできる気がしたので、あまり他のやり方を比較せずに決めました。

# ディレクトリ構成
```
vue
├ _docker
│ ├ Dockerfile
│ └ compose.yaml
└ app
```

# Docker準備

https://zenn.dev/chida/articles/8f16e42364398c

こちらの記事を大いに参考にしつつ、「とりあえずの環境」をさくっと作ってしまいます。

## Dockerfile

```Dockerfile
FROM node:14.17.1
WORKDIR /src
RUN yarn global add @vue/cli
```
参考記事のコードをそのまま使用しているためv14ですが、最新はv16のようですね。会社で使っているのは何かもわからない、そしてとりあえずの基本を知りたいだけなので、このまま進めます。

https://nodejs.jp/

node14のimageに既にyarnがあるため、インストールせずにvueを使うためのコマンドを使えます。

## compose.yaml

```docker-compose.yaml
services:
  app:
    build: .
    container_name: vue-project
    ports:
      - '8081:8080'
    volumes:
      - ../app:/src
    tty: true
```

同ディレクトリにあるDockerfileを参照し、ポートは8080を使用中ですので8081に。バインドマウントで../appにてファイルの編集が可能になります。

バインドマウントをしておけば、ファイルを更新するだけで変更が反映されるので便利です。vueは特に何の設定もせず、単にファイルの保存だけで更新できるので、楽です。

## docker composeでコンテナ起動
compose.yamlのあるディレクトリ(_docker内)で以下のコマンドを実行します。
私の場合、VSCodeで作業をしますので、該当のディレクトリで右クリックをして、「Open in Integrated Terminal」(日本語だと「ターミナルで開く？」)で開くのが楽で好きです。
<img width="300" alt="VSCode ターミナルで開く" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/925094/8dab4577-6257-c56b-68a6-6271f487b474.png">


```
docker compose -p vue up -d
```
私はコンテナを起動するときは、たいていプロジェクト名をつけています。「-p vue」のところですね。「vue」という名前のプロジェクトです。こうしないと、他のコンテナを起動する際に問題になります。複数のプロジェクトを同時に進めるときは必須かと。

コンテナが起動されます。

# Vueプロジェクトの作成
コンテナ内でプロジェクトの作成をします。以下のコマンドでコンテナ内でコマンドを使用できます。
```
docker compose -p vue exec app /bin/bash
```
DockerfileでWORKDIRをsrcに指定しているので、execでコンテナ内に入ると自動的にバインドマウントをしているsrcディレクトリで作業ができます。cdでの移動は不要です。

以下のコマンドでプロジェクトを作成します。

```
vue create .
```

```
Vue CLI v5.0.8
? Generate project in current directory? (Y/n) 
```
今いるディレクトリにプロジェクトを作って良いか？ → OK!

```
? Please pick a preset: (Use arrow keys)
❯ Default ([Vue 3] babel, eslint) 
  Default ([Vue 2] babel, eslint) 
  Manually select features 
```
とりあえずVue 3で! 上下の矢印で選び、エンターで選択できます。Vue3なら動かさずそのまま。

```
? Pick the package manager to use when installing dependencies: (Use arrow keys)
❯ Use Yarn 
  Use NPM 
```
参考ページのそのまま、yarnを。エンター。画面が動いてプロジェクトが作成されます。

```
 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.
```

これについてはちょっと謎なので保留。gitの設定しなければなのかな？無視しても良いかと。

ここまできたら、vueを起動させます。
```
yarn serve
```

ブラウザにて「localhost:8081」（compose.yamlで指定したポート）にアクセスすると、以下の画面が表示されれば成功です。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/925094/77e5fbbc-ec37-9342-6366-2d4d69a3f1b0.png" alt="vue起動後画面" width="300">

# VSCodeの拡張機能

以下2つを入れました。

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

https://marketplace.visualstudio.com/items?itemName=octref.vetur


# 公式チュートリアルで学ぶ

本家

https://vuejs.org/tutorial/#step-1

日本語版

https://ja.vuejs.org/tutorial/#step-1

15ページのチュートリアルです。これはブラウザにコードを入れると、ブラウザでプレビューもできるすぐれものです。が、Dockerで環境設定をしたので、実際にこのコードで動かすことになります。

とりあえず、最初のページをチュートリアルの内容に変更します。

ところで、vue createで作成されたファイルは以下のようなディレクトリになっています。
```
app
├ .git
├ node_modules
├ public
├ src
│ ├ assets
│ ├ components
│ │ └ HelloWorld.vue
│ └ App.vue
├ .gitignore
├ babel.config.js
├ jsconfig.json
├ package.json
├ README.md
├ vue.config.js
└ yarn.lock
```

localhost:8081の表示内容は、src内のApp.vueを編集することで変更できます。ここでチュートリアルの内容をやっていこうと思います。

## 1 はじめに
最初のステップは、チュートリアルの使い方等でそんなにすることはありません。しかし、コードを書く箇所「App.vue」に以下のように書かれており、「PREVIEW」に太字の「Hello World!」が表示されています。

```App.vue
<template>
  <h1>Hello World!</h1>
</template>
```

vueでは、表示する内容を<template>で囲むんだな、ということがわかります。
画面左の右下の「Next」で次に進みます。

## 2 宣言的レンダリング Decrative Rendering

ここからは実際にコードを書きながら学んでいきますが、ページを開いた時のコードを書く場所は、コードが途中まで書かれています。ここに追記して、指示通りの表示ができるか、というふうに進めていきます。

私の場合、プログラミング言語を学ぶときは、あまり説明を読まずにひたすら写経（コードを書き写す）するスタイルなので、画面左下の「Show me!」で答えを表示させてしまい、それをただ写経します。

```App.vue

<script setup>
import { reactive, ref } from 'vue'

const counter = reactive({ count: 0 })
const message = ref('Hello World!')
</script>

<template>
  <h1>{{ message }}</h1>
  <p>Count is: {{ counter.count }}</p>
</template>
```

変数を使って、文字を表示させたり、数字が変化していく変数も表示させられるんだな〜ということを学びます。
template内に変数を表示させるときは{{ }}で囲むのね。Laravelと同じだなあ。

## 3 属性バインディング Attribute Bindings
```App.vue
<script setup>
import { ref } from 'vue'

const titleClass = ref('title')
</script>

<template>
  <h1 :class="titleClass">Make me red</h1>
</template>

<style>
.title {
  color: red;
}
</style>
```
h1タグの中の「:class=""」というところが、今までにないvueっぽい記述です。こうやってクラスを指定できるのか〜。

## 4 イベントリスナー Event Listeners

```App.vue
<script setup>
import { ref } from 'vue'

const count = ref(0)

function increment() {
  count.value++
}
</script>

<template>
  <button @click="increment">Count is: {{ count }}</button>
</template>

```
ボタンを押すと、ボタンの数字がどんどん増えてくるプログラムです。普通のHTML、JSだと「onclick」で指定していたものを「＠click」で指定するんだな。

## 5　フォームバインディング

```App.vue
<script setup>
import { ref } from 'vue'

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

<template>
  <input v-model="text" placeholder="Type here">
  <p>{{ text }}</p>
</template>
```
フォームに入れた内容が、瞬時に反映されている！「v-model」という新しい要素が登場。

## 6 条件付きレンダリング
vueのif文が登場です。

```App.vue
<script setup>
import { ref } from 'vue'

const awesome = ref(true)

function toggle() {
  awesome.value = !awesome.value
}
</script>

<template>
  <button @click="toggle">Toggle</button>
  <h1 v-if="awesome">Vue is awesome!</h1>
  <h1 v-else>Oh no 😢</h1>
</template>
```
HTMLタグの中にifの条件を書き込むという発想が面白い。が、ちょっと戸惑いもある。elseも書ける。これは両方h1に書いているけど、チュートリアルの別のところではifとelseが違うタグの中に書かれていて、ぱっと見わかりづらいと思いました。

## 7 レストレンダリング
ループです。for文もif文同様、HTMLタグ内に書きます。
```App.vue
<script setup>
import { ref } from 'vue'

// give each todo a unique id
let id = 0

const newTodo = ref('')
const todos = ref([
  { id: id++, text: 'Learn HTML' },
  { id: id++, text: 'Learn JavaScript' },
  { id: id++, text: 'Learn Vue' }
])

function addTodo() {
  todos.value.push({ id: id++, text: newTodo.value })
  newTodo.value = ''
}

function removeTodo(todo) {
  todos.value = todos.value.filter((t) => t !== todo)
}
</script>

<template>
  <form @submit.prevent="addTodo">
    <input v-model="newTodo" required placeholder="new todo">
    <button>Add Todo</button>
  </form>
  <ul>
    <li v-for="todo in todos" :key="todo.id">
      {{ todo.text }}
      <button @click="removeTodo(todo)">X</button>
    </li>
  </ul>
</template>
```

…まああとはこんな感じでチュートリアルをすすめて、一つ一つの機能を体感していこうと思います。

# まとめ

Vue.jsを勉強する際、Dockerを使用すると簡単に環境設定ができると感じました。そして、公式チュートリアルをこなすことで、初歩の初歩の知識は身に付くような気がしました。

まだ私自身学習が始まったばかり。勉強を続けなければすぐにここまでの流れを忘れそうですので、いつでも再度やり直すように記事にまとめられてよかったです。