Docker Compose 上で Vue CLI の Hot Reload を動くようにする

## 結論

Docker Compose上でVue CLIの`npm run sreve`をすると、開発サーバーのホットリロードがうまく動作しない。
原因は、ブラウザからアクセスする開発サーバーのWebSocketのURLが正しくないことが原因。
プロジェクトルートに`vue.config.js`をなければ作り、以下のように書くことで解決できる。

```js:vue.config.js
module.exports = {
  devServer: {
    client: {
      host: '0.0.0.0',
      webSocketURL: 'ws://0.0.0.0:8080/ws'
    }
  }
}
```

WebPackのバージョンが4なら、以下で動くと思われる(確認していないので不明)。
参考: https://stackoverflow.com/questions/71817054/dockerized-vue-app-hot-reload-does-not-work

```js:vue.config.js
module.exports = {
  devServer: {
    public: '0.0.0.0:8080'
  }
}
```


# 説明: 実際にやってみる
### 環境
- macOS Big Sur 11.4
- @vue/cli 5.0.7
- Docker version 20.10.17
- docker-compose version 1.29.2

## Dockerなしで動かす
Vue CLIでプロジェクトを作成する
```
vue create setsumei
```
開発サーバーを立ち上げる
```
cd setsumei
npm run serve
```
実行結果
```
⋊> npm run serve

> sestumei@0.1.0 serve
> vue-cli-service serve

 INFO  Starting development server...


 DONE  Compiled successfully in 2406ms 


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

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

```

### ブラウザで確認してみる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/282870/c1345aa2-9fb7-9b0f-c82e-ffbd7bfb79cf.png)
開発者ツールを開いて、ネットワークを見てみる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/282870/898d7757-ce60-3f9b-19e8-f4f716fd6766.png)
![Screen Shot 2022-07-15 at 15.48.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/282870/5a0b2050-f0f5-5dc7-e7ae-709e48946f59.png)
WebSocketが動いている！
HotReloadはWebSocketを使うことよって実現されている。
WebSocketの接続先のURLをみると、`loclhost:8080`にアクセスしているのに、WebSocketは192.168.3.10:8080にアクセスしている。
これは、`npm run serve`した時にコンソールに出るNetworkのURLとなっている。


## Docker Composeで動かす
プロジェクトのルートに`docker-compose.yaml`を作成
```yaml:docker-compose.yaml
version: "3.9"
services:
  app:
    image: node:16-alpine
    command: npm run serve
    ports:
      - 8080:8080
    working_dir: /app
    volumes: 
      - .:/app

```
立ち上げる
```
⋊> docker-compose up

Creating network "sestumei_default" with the default driver
Creating sestumei_app_1 ... done
Attaching to sestumei_app_1
app_1  | 
app_1  | > sestumei@0.1.0 serve
app_1  | > vue-cli-service serve
app_1  | 
app_1  |  INFO  Starting development server...
 DONE  Compiled successfully in 24505ms12:34:56 AM
app_1  | 

app_1  |   App running at:
app_1  |   - Local:   http://localhost:8080/ 
app_1  |   - Network: http://172.19.0.2:8080/
app_1  | 
app_1  |   Note that the development build is not optimized.
app_1  |   To create a production build, run npm run build.
app_1  | 
Build finished at 12:34:56 by 0.000s
```
### ブラウザで確認してみる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/282870/df216908-955c-3754-e104-c6216d5c08aa.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/282870/fe7a968a-1dd0-88ed-a105-b5f366e95b7a.png)
**WebSocketの接続に失敗している😞**

### 原因と解決策
原因は、URLがDockerのネットワークの中のURLになっているから、本来は`localhost:8080`に接続して欲しい。
開発サーバーのWebSocketのURLを変えるには、`vue.config.js`を編集する。
```diff:vue.config.js
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
+ devServer: {
+   client: {
+     host: '0.0.0.0',
+     webSocketURL: 'ws://0.0.0.0:8080/ws'
+   }
  }
})
```
### もう一度Docker Composeで動かす
```
⋊> docker-compose up

Creating network "sestumei_default" with the default driver
Creating sestumei_app_1 ... done
Attaching to sestumei_app_1
app_1  | 
app_1  | > sestumei@0.1.0 serve
app_1  | > vue-cli-service serve
app_1  | 
app_1  |  INFO  Starting development server...
 DONE  Compiled successfully in 19368ms12:34:56 AM
app_1  | 

app_1  |   App running at:
app_1  |   - Local:   http://localhost:8080/ 
app_1  |   - Network: http://172.20.0.2:8080/
app_1  | 
app_1  |   Note that the development build is not optimized.
app_1  |   To create a production build, run npm run build.
app_1  | 
Build finished at 12:34:56 by 0.000s

```
### ブラウザで確認してみる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/282870/061b95b4-3ba8-c256-4e2a-ffa0b071ec50.png)
![Screen Shot 2022-07-15 at 16.20.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/282870/6e2811ac-2e70-1cd3-d267-6bd06715097a.png)
**localhostにWebSocketのURLが変わった🎉**

## 参考
https://cli.vuejs.org/config/#devserver

https://webpack.js.org/configuration/dev-server/#websocketurl

webpackのドキュメントのwebSocketURLのところに書いてある。
>**webSocketURL**
>`string` `object`
>This option allows specifying URL to web socket server (useful when you're proxying dev server and client script does not always know where to connect to).