axiosで叩くAPIのホストを環境変数に基づいて変える(Webpack/Vue CLI3)

ごめんなさい最初に書いたやり方やっぱりそんなにイケてないです
下の方に追記したdotenv使う方法が一番よさげでした

# ダメな例
```vuejs:Hello.vue
<template>
  <div>
    {{ response }}
  </div>
</template>

<script>
import axios from 'axios'

export default {
  name: 'hello',
  data () {
    return {
      response: '',
    }
  },
  mounted () {
    axios.defaults.baseURL =
      process.env.VUE_APP_ENV === 'production' ? 'http://localhost:8001'
      : process.env.VUE_APP_ENV === 'staging' ? 'http://localhost:8002' 
      : 'http://localhost:8003'
    this.$axios.get('/get.json').then(response => (this.info = response))
  }
}
</script>
```

```shell-session:ビルド時
$ export VUE_APP_ENV=production
$ npm run build
```

ビルドされたjsファイルにstagingやdevelopment用のAPIのホスト名も書かれてしまうのでこれはNG

ってことで以下のやり方にしましょう

# いい例
```js:vue.config.js
const webpack = require('webpack')
module.exports = {
  configureWebpack: {
    plugins: [
      new webpack.DefinePlugin({
        'VUE_APP_API_ENDPOINT': JSON.stringify(
          process.env.VUE_APP_ENV === 'production' ? 'http://localhost:8001'
          : process.env.VUE_APP_ENV === 'staging' ? 'http://localhost:8002'
          : 'http://localhost:8003'
        )
      })
    ]
  },
```

```vuejs:Hello.vue
<template>
  <div>
    {{ response }}
  </div>
</template>

<script>
import axios from 'axios'

export default {
  name: 'hello',
  data () {
    return {
      response: '',
    }
  },
  mounted () {
    axios.defaults.baseURL = VUE_APP_API_ENDPOINT
    this.$axios.get('/get.json').then(response => (this.info = response))
  }
}
</script>
```

## もしくは
これでもいい

```js:vue.config.js
module.exports = {
  chainWebpack: config => {
    config
      .plugin('web-component-options')
      .use(require('webpack/lib/DefinePlugin'), [{
        'VUE_APP_API_ENDPOINT': JSON.stringify(
          process.env.VUE_APP_ENV === 'production' ? 'http://localhost:8001'
          : process.env.VUE_APP_ENV === 'staging' ? 'http://localhost:8002'
          : 'http://localhost:8003'
        )
      }])
  }
}
```
# 参考資料
- https://cli.vuejs.org/config/#configurewebpack
- https://cli.vuejs.org/guide/webpack.html#simple-configuration
- https://cli.vuejs.org/config/#chainwebpack
- https://cli.vuejs.org/guide/webpack.html#chaining-advanced


Vue CLI3は2系に比べるとWebpackの設定が隠されてて苦労しました

セキュリティ意識と給料は高いに越したことはないですね

# 追記
上記だと動作は問題ないけどLinterが
`VUE_APP_API_ENDPOINT`が未定義だよって怒るので結局dotenvがいいのかもしれない

## dotenvで環境ごとに切り替える方法
`.env.[mode]`というファイルを用意する

例えば
- `.env.development` => developmentモードの時に読み込まれる
- `.env.staging` => stagingモードの時に読み込まれる
- `.env.production` => productionモードの時に読み込まれる
- `.env` => モード関係なく読まれる(共通の項目を書くといい)

下に貼ったリンクの公式ドキュメント見ると`development`/`production`/`test`だけが取り上げられてるけど`staging`もいけるみたい

ただ、環境変数`NODE_ENV`は`development`/`production`のどちらかにしとかないとビルド時の設定が変わるらしい(optimizedとか)

### modeの指定
`serve`や`build`の時に`--mode development`とかってしてやればいいけど

```shell-session
$ npm run server --mode development
```

だと引数をちゃんと拾ってくれないので

```shell-session
$ ./node_modules/.bin/vue-cli-service serve --mode staging
```

と叩くか、もしくは`package.json`にスクリプトを追加する

```js:package.json
.
.
.
  "scripts": {
    "serve:dev": "vue-cli-service serve --mode development",
    "serve:stg": "vue-cli-service serve --mode staging",
    "serve:prd": "vue-cli-service serve --mode production",
    "build:dev": "vue-cli-service build --mode development",
    "build:stg": "vue-cli-service build --mode staging",
    "build:prd": "vue-cli-service build --mode production",
    "lint": "vue-cli-service lint"
  },
.
.
.
```

### 環境変数の制約
基本定数にはプレフィックス`VUE_APP_`をつけなきゃいけないけど
`NODE_ENV`は許されるっぽい

## 以上を踏まえてどうしたらいいのかっていうと
### 1. 下記ファイルを作成
- .env.development
- .env.staging
- .env.production
- .env

### 2. 下記ファイルを修正
- package.json

### 3. vueソース内では`process.env.VUE_APP_API_ENDPOINT`をはめ込む

## こんな感じかな
```:.env
# 共通の設定を書く
VUE_APP_COMMON=common
```

```:.env.development
# developmentの設定を書く
NODE_ENV=development
VUE_APP_API_ENDPOINT=http://localhost:3000
```

```:.env.staging
# stagingの設定を書く
NODE_ENV=production
VUE_APP_API_ENDPOINT=http://stg.api-server-domain
```

```:.env.production
# productionの設定を書く
NODE_ENV=production
VUE_APP_API_ENDPOINT=http://api-server-domain
```

```js:package.json
.
.
.
  "scripts": {
    "serve:dev": "vue-cli-service serve --mode development",
    "serve:stg": "vue-cli-service serve --mode staging",
    "serve:prd": "vue-cli-service serve --mode production",
    "build:dev": "vue-cli-service build --mode development",
    "build:stg": "vue-cli-service build --mode staging",
    "build:prd": "vue-cli-service build --mode production",
    "lint": "vue-cli-service lint"
  },
.
.
.
```

```vuejs:Hello.vue
<template>
  <div>
    {{ response }}
  </div>
</template>

<script>
import axios from 'axios'

export default {
  name: 'hello',
  data () {
    return {
      response: '',
    }
  },
  mounted () {
    axios.defaults.baseURL = process.env.VUE_APP_API_ENDPOINT
    this.$axios.get('/get.json').then(response => (this.info = response))
  }
}
</script>
```
### NODE_ENVについては
modeオプションとは別に、明示的に定義しないと
stagingは`NODE_ENV=development`としてビルドされてしまうため
`NODE_ENV=production`で定義してる

ローカルでビルドした結果を確認したい時には
`.env.development`も`NODE_ENV=production`にしたりしなきゃいけないだろうから
全部`NODE_ENV=production`でもいいかも

それなら`.env.development`にだけ`NODE_ENV`を書かずに
npm scriptsでexportしちゃうとか

```js:package.json
.
.
.
  "scripts": {
    "serve:dev": "export NODE_ENV=development; vue-cli-service serve --mode development",
    "serve:stg": "vue-cli-service serve --mode staging",
    "serve:prd": "vue-cli-service serve --mode production",
    "build:dev": "export NODE_ENV=production; vue-cli-service build --mode development",
    "build:stg": "vue-cli-service build --mode staging",
    "build:prd": "vue-cli-service build --mode production",
    "lint": "vue-cli-service lint"
  },
.
.
.
```

いっそのことstgもprdも`NODE_ENV`はnpm scriptsに書いた方がいい気がしてきた

# 〆
ってことで
これでlinterに文句は言われず、ビルドされたjsにも他環境の情報が載らないのでこっちの方がよさげですね

他にいい方法あったら教えてくださいフロントエンドの神様

## 参考記事
-　https://cli.vuejs.org/guide/mode-and-env.html#environment-variables