【Vue.js】vue-cliで外部JSファイルを読み込む方法

# vue-cliで外部JSファイルを読み込む方法

## 背景
Vueコンポーネントの年月日のプルダウンを外部ファイルのjsで実装したい

## 理由
可読性やメンテナンス性が向上するため
大規模なプロジェクトになるほど、こういった構成にしたときのメリットを感じやすい

## 準備
vueCLIのディレクトリのsrcフォルダ直下にutilesという名前のフォルダを作成
その中にdefinition.jsとういう外部JSファイルを作成

## 実装

### 外部js
1. 外部ファイル内に年月日の値を生成する（関数を作成、ループ関数を配列に格納する←どちらでも可能だが、definitionsの役割は値の生成なので、jsファイル側で格納する）
2. 年月日それぞれの値をexport

```definition.js
var this_year, today;
today = new Date();
this_year = today.getFullYear();

// 配列を変数に格納
const yearList = []
const monthList = []
const dayList = []

// 第一引数〜第二引数までの年月日を生成し、配列（list）に追加する関数を作成
const optionLoop = (start, end, list) => {
    for( let i = start; i <= end; i++)
        list.push(i)
}

optionLoop(1950, this_year, yearList);
optionLoop(1, 12, monthList);
optionLoop(1, 31, dayList);

// 年月日の値のforループを配列に格納し、vueコンポーネントにexportする
export {yearList, monthList, dayList}
```
### 読み込む側のVueコンポーネント
1. ```import { yearList, monthList, dayList } from '@/utiles/definition';```で外部jsファイルを読み込む
2. dataプロパティ部分でv-forで回す際の配列（years, months, days）に値をセットする

※yearList, monthList, dayListはそれぞれ年月日の値が入っている配列である

```BasicForm.vue


<template>
<div>
  <div class="form">
    <div class="header">
      <p id="step">STEP1</p>
      <p id="inst">お客様の情報を入力してください</p>
    </div>
    <div class="body">
      <p class="genre">-性別-</p>
        <label><input type="radio" name="gender" value="男性" @change="updateGender">男性</label>
        <label><input type="radio" name="gender" value="女性" @change="updateGender">女性</label>
      <p class="genre">-生年月日-</p>
      <select name="year" id="id_year" @change="updateYear">
        <option v-for="(year, key) in years" :key="key">{{ year }}</option>
      </select>年
      <select name="month" id="id_month" @change="updateMonth">
        <option v-for="(month, key) in months" :key="key">{{ month }}</option>
      </select>月
      <select name="day" id="id_day" @change="updateDay">
        <option v-for="(day, key) in days" :key="key">{{ day }}</option>
      </select>日
    </div>
  </div>
  <div class="button-group">
    <router-link to="/questionnaire" class="button">次へ進む ＞</router-link>
  </div>
</div>
</template>

<script>
import { yearList, monthList, dayList } from '@/utiles/definition';

export default {
  data() {
    return {
      years: yearList,
      months: monthList,
      days: dayList,
    }
  },
  methods: {
    updateGender (e) {
      this.$store.commit('updateGender', e.target.value)
    },
    updateYear (e) {
      this.$store.commit('updateYear', e.target.value)
    },
    updateMonth (e) {
      this.$store.commit('updateMonth', e.target.value)
    },
    updateDay (e) {
      this.$store.commit('updateDay', e.target.value)
    },
  }
}
</script>
  <!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
* {
  // outline: auto;
	margin:0; padding:0; 		/*全要素のマージン・パディングをリセット*/
  max-width: 100%;
}

#step{
  background: #1e90ff;
  color: #fff;
  font-size: 2px;
  margin: 0;
  max-width: 40px;
  padding: 2px;
  text-align: center;
}

#inst {
  font-size: 12px;
  color: #696969;
  text-align: center;
  padding-bottom: 10px;
}

.header {
  background: #afeeee;
  margin: 0%;
  padding: 0%;
  line-height: 10px;
  border-bottom: solid 1px #48d1cc;
}

.body {
  font-size: 9px;
  margin: 10px;
  padding-bottom: 10px;
  line-height: 30px;
  text-align: left;
}

.body input {
  vertical-align:middle;
}

.genre {
  color: #1e90ff;
}

select {
  padding: 5px 10px 5px 0px;
  font-size: 9px;
  border: 1px solid #dcdcdc;
}

.form{
  max-width: 80%;
  margin-top: 80px;
  margin-left: auto;
  margin-right: auto;
  border: 1px solid #48d1cc;
  border-bottom: 0.5px solid #000;
}

.button-group{
  text-align: center;
}

.button {
  background-color: #40e0d0;
  color: #fff;
  border: solid 1px #48d1cc;
  margin-top: 1rem;
  padding: 5px 10px;
  border-radius: 0.5rem 0.5rem;
  text-decoration: none;
  display:inline-block;
  margin: 10px;
}
</style>

```

## おまけ
プルダウンは、コンポーネント内だけで完結させる（[例](https://algorithm.joho.info/typescript/vue-js-select-date/)）こともできるが、可読性やメンテナンス性向上のため、値を生成するjsファイルを用意するという方が適切だと判断し、今回実装を行った。