Vue.jsについての基礎(基本構文)

#はじめに
おはようございます。こんにちは。こんばんは。
ワタタクです。
今回はVue.jsにの基本構文ついて見けいけたらいいなと書いています。
Vue.jsに関しては少し触ったことがある程度なので、今後の為にと勉強させていただきますので
もし間違いとかありましたらご教授、アドバイスなんかいただけたら幸いでございます。
では早速参ります。


#基本構文
<font color="red">**※[Vue.jsについての基礎(インストール)](https://qiita.com/watataku/items/26f2ce546fcd562e4b46)記事を読んだ前提で書いています。**</font>

インストール編でプロジェクトを作成すると、いろんなファイルが出来ててキョどりますが大丈夫です。
基本的に難しい設定をしなければ、だいたいsrcディレクトリの中のVueファイルだけをいじるだけです。
##v-showディレクティブ
まず最初に要素を表示するかどうかを決める条件付きレンダリングです。

```vue:HelloWold.vue
<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
<!--////////////////追加/////////////////-->
    <h2 v-show="showText">表示項目</h2>
<!--/////////////////////////////////-->
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data () {
    return {
      msg: 'Welcome to Your Vue.js App',
//----------------------追加----------------------
      showText: false
//-----------------------------------------------
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>

</style>
```
v-showディレクティブはタグの中にいれて書きます。上のコード内ではイコールでshowTextという変数をみてます。この変数の名前は任意ですが、*これがfalseなら非表示、trueなら表示となります。*

##イベントハンドリング
ボタンをクリックしたら表示、非表示が切り替わる仕組みを作ります。
早速コードを見てみましょう。

```vue:HelloWold.vue
<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
<!--////////////////追加/////////////////-->
    <button v-on:click="toggle">toggle</button>
<!--/////////////////////////////////-->
    <h2 v-show="showText">表示項目</h2>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data () {
    return {
      msg: 'Welcome to Your Vue.js App',
      showText: false
    }
  }//追加
  ,methods: { 
    toggle: function () {
      this.showText = !this.showText
    }
  }
   //-----------------------------------------------
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>

</style>
```
###v-onディレクティブ
buttonタグにv-on:clickというものがついてます。*v-onでイベント発火時の JavaScript の実行が可能になります。clickしたら呼ばれる関数名をtoggleにしてます。これはmethodsオブジェクトの中で定義されています。*
<font color="orange">**v-on:は@とも書ける**</font>

###methodsオブジェクト
methodsオブジェクトの中に実行したい関数をガシガシ定義していきます。これをv-on:clickで読んだり、他の関数から叩いたりします。

###thisについて
data内で定義した値はthis.showTextのようにthisを使って参照していきます。
##v-modelディレクティブ
v-modelはVue.jsを使ってフォームを構築する際によく使う機能です。
v-modelはv-onとv-bindをまとめて一行で書くためのシュガーシンタックスです。
例としてフォームに入力した文字数をカウントする仕組みを作ります。

```vue:HelloWould.vue
<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
<!--////////////////追加/////////////////-->
    <input type="text" name="text" v-model="count">
    <h3>現在の文字数：{{ charaCount }}文字</h3>
<!--/////////////////////////////////-->

  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data () {
    return {
      msg: 'Welcome to Your Vue.js App',
      //追加
      count: ''
    }
  }
  ,//追加
  computed: {
    charaCount: function() {
        return this.count.length;
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>

</style>
```
ここで重要なのは、**「computed」**です。
computedという機能を使えば、<font color="orange">dataで保持している値に変更がある度に、それを利用した値をリアルタイムに生成</font>できます。

ついでにもう一つ。
computedに似ているのですが、**「watch」**という機能を紹介します。
watchは、<font color="orange">dataで保有している値に１対１で対応して、その値が変更されたときに動作するメソッド</font>のようなものです。
Vue公式ガイドではwatchよりもcomputedを推してます。

以上が双方向データバインディング(v-model)の基本的な使い方で、webアプリを作成するときには非常によく使う手法ですので、必ずマスターしてください。
###[TIPS]
- 「watch」でオブジェクトの変更を監視する方法。
watchオプションを利用すると、データの変化を監視して、関数を実行することができます。このような機能は「ウォッチャ」および「監視プロパティ」と呼ばれています。

```js
export default {
  data() {
    監視したいもの
  },
  watch: {
    監視したいもの: {
      handler: function () {
          console.log("監視したいものに変更がありました。")
      },
      deep: true
    }
  }
}
```
上記のようにすると監視したい値に変更があると、watchが動きます。

##v-forディレクティブ
v-forディレクティブを利用して、配列の要素を繰り返し表示させてみます。

```vue:HelloWold.vue
<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
<!--////////////////追加/////////////////-->
    <table>
          <thead>
              <tr>
                  <th>タイトル</th>
                  <th>著者</th>
              </tr>
          </thead>
          <tbody>
              <tr v-for="book in books" v-bind:key="book.id">
                  <td>{{ book.title }}</td>
                  <td>{{ book.author }}</td>
              </tr>
          </tbody>
      </table>
<!--/////////////////////////////////-->
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data () {
    return {
      msg: 'Welcome to Your Vue.js App',
      //追加
      books: [
          { id: 1, title: '坊っちゃん', author: '夏目漱石' },
          { id: 2, title: '人間失格', author: '太宰治' },
          { id: 3, title: 'ノルウェイの森', author: '村上春樹' }
      ]
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>

</style>
```
booksプロパティとしての配列からbookという変数名に置き換えたオブジェクトを取得しています。v-forディレクティブは一般的に下記のような記述をします。

```vue
v-for="変数名 in 配列やオブジェクト"
```
また、<font color="red">**v-forディレクティブを利用する場合、v-bindディレクティブで一意の（ユニークな）keyプロパティを設定する必要があります。**</font>
最低、今までのことが理解できればVue.jsの基本構文は大丈夫だと思います。

あとの描き方なんかは公式サイト参照でお願いします。

#コンポーネント
初めて触る人に対して、一番理解に苦しむと思いますが、(作者は苦労しました。)
Vue.jsに限らず、Reactなどのjavascriptフレームワークでは必須の考え方なので理解しておきましょう。

ここからはGoogleMapAPIを用いた地図アプリを作りながらコンポーネントを解説していきます。
その前に、以下のコマンドを入力してください。

```console
$ npm install --save materialize-css@1.0.0-rc.1
```

次に「main.js」に以下を記述し、vue-routerの設定をコメントアウトで潰してください。

```js;main.js
import Vue from 'vue'
import App from './App'
//import router from './router'
<!--//////////////　追加　///////////////////////-->
import 'materialize-css'
import 'materialize-css/dist/css/materialize.min.css'
<!--/////////////////////////////////////-->
Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  //router,
  components: { App },
  template: '<App/>'
})
```
その次に、「index.html」にGoogleMapApiを読み込んでください。

これで準備が整いました。
では、「components/Map.vue」をつくります。
作れたら、App.vue（親コンポーネント）に以下のコードを記述してください。

```vue:App.vue
<template>
  <div id="app">
    <nav class="blue navbar">
      <div class="nav-wrapper">
        <a href="#" class="brand-logo center"><i class="material-icons left" >ヘッダーだよん！！</i></a>
      </div>
    </nav>

    <!-- Mapをレンダリング -->
    <Map v-bind:center="center"></Map>
  </div>
</template>

<script>
// Mapを読込
import Map from './components/Map.vue';

export default {
  name: 'app',
  components: {
    Map
  },

  data () {
    return {
     // 地図のセンター位置(東京駅)
     //{ lat: 緯度, lng: 経度 }
     center: { lat: 35.681298, lng: 139.7640529 }
    }
  },
}
</script>

<style>
  
</style>
```
ここで重要なのは

```js
import Map from './components/Map.vue';
```

と

```js
components: {
    Map
}
```
です。以下で子コンポーネント(Map)を読み込んでいます。
そして``<Map v-bind:center="center"></Map>``でレンダリングしています。

**※v-bind, :value**
親 -> 子にデータを受け渡す際、親側で記述します。親からデータを渡すための窓口のイメージです。

``v-bind:プロパティ名``もしくは``:プロパティ名``という書き方をします。プロパティ名は、後述する子要素のpropsの変数名のことです。

つまり、``v-bind:プロパティ名 = "値"``で子のコンポートに値を送ることになります。

次に「components/Map.vue」です。（子コンポネート）

```vue:Map.vue
<template>
  <div class="main-area">
    <div class="main-area-inner">
      <div id="map" ref="map"></div>
    </div>
  </div>
</template>

<script>
  export default {

    // 親のコンポーネントからpropsでdataを受け取る
    props: ['center'],

    data () {
      return {
        // Map Objectを保存する
        map: null,
        // Markerオブジェクトを配列で保存する
        markers: [],
      }
    },

    mounted () {
      // Mapの初期処理を実行する
      const map = this.$refs.map;
      this.map = new window.google.maps.Map(map, {
        center: this.center,
        zoom: 17
      });

      // マーカーを画面上に置く
      this.markers = [];
      const marker = new window.google.maps.Marker({
        position: {
          lat: this.center.lat,
          lng: this.center.lng
        },
        map: this.map,
        animation: google.maps.Animation.DROP
      });
     }

  }

</script>

<style>

</style>
```
ここで重要なのは``props``。

``props``は、親 -> 子にデータを受け渡す際、子側で記述します。子がデータを受け取るための窓口のイメージです。

##非同期通信と$emit
まず、今回非同期通信は``axios``を使います。

##インストール

```console
$ npm install --save axios
```
main.jsにてaxiosを取り込みます。これで**すべてのコンポーネントにおいて**「this.$axios」でaxiosが利用できるようになります。

```js:main.js
import Vue from 'vue'
import App from './App'
//import router from './router'

import 'materialize-css';
import 'materialize-css/dist/css/materialize.min.css'

import axios from 'axios' //追加

Vue.config.productionTip = false

Vue.prototype.$axios = axios //追加

/* eslint-disable no-new */
new Vue({
  el: '#app',
  //router,
  components: { App },
  template: '<App/>'
})
```
※任意のコンポーネントの中にてaxiosをインポートすることも可能です。<font color="red">その場合は「this.$axios」ではなく「axios」となります。</font>

さて先ほど作ったマップアプリを使いながら``axios``、``$emit``の解説をしていきましょう。
※今回、楽天トラベルAPIを使います。

前回、行ったように「components/Card.vue」を作成し、コンポーネントを読みこんでください。

```vue;Card.vue
<template>
  <div class="card">
      <div class="row">
        <div class="col s3 image">
          <img v-bind:src="this.rankings.hotelImageUrl" class="responsive-img" />
        </div>
        <div class="col s9 content">
            <i class="left">ランキング：<font color="red">{{ this.rankings.rank }}</font></i>
            <i class="left">ホテル名：{{ this.rankings.hotelName }}</i>
            <i class="left">都道府県：{{ this.rankings.middleClassName }}</i>
        </div>
      </div>
  </div>
</template>

<script>

  export default {
    data () {
      return {
        rankings: ""
      }
    },
    mounted () {
      // 楽天APIからデータを取得する
      this.searchPlans();
    },
    methods: {
      // 楽天APIに接続して、データを取得する
      async searchPlans () {
        await this.$axios.get(" 楽天APIからのURL ",{
        }).then((resp) => {
          this.rankings = resp.data.Rankings[0].Ranking.hotels[0].hotel
        
          this.$emit("ranking", resp.data.Rankings[0].Ranking);
        }).catch(err => {
          console.log(err);
        });
      }
    }
  }
</script>
　
<style scoped>
  
</style>
```
```vue:App.vue
<template>
  <Card v-on:ranking="getRanking"></Card>
</template>

<script>
data () {
    return {
      rankings: [],
    }
  },
  methods: {
    getRanking(ranking) {
      this.rankings = ranking;
    },
  }
}
</script>

<style>

</style>

```
ここで重要となってくるのが``axios(非同期通信)の書き方``と``this.$emit``の使い方です。

まず``axios``の書き方は

```js
//GET
axios.get('URL', {
  params: {
    キー: 値
  }
})
.then(function (response) {
  console.log(response);
})
.catch(function (error) {
  console.log(error);
});

//※これでも可
axios.get('/URL?キー: 値')
  .then(function (response) {
console.log(response);
})
.catch(function (error) {
  console.log(error);
});

//POST
axios.post('URL', {
  キー: 値,
  キー: 値
})
.then(function (response) {
  console.log(response);
})
.catch(function (error) {
  console.log(error);
});
```
です。

続いて``this.$emit``の説明をします。
簡単にいうと、子 -> 親にデータを受け渡しするときに使います。
例えば、子コンポネートの変数``hoge``の値を親コンポーネントに渡したい時は

```js:子コンポート
this.$emit("イベント名", hoge);
```
*指定したイベント名にhogeという値が入ってくるイメージ*
として、親コンポートで

```vue:親コンポーネント
<Template>
  <子コンポーネント v-on:イベント名:"関数"></子コンポーネント>
</Template>
<script>
data () {
  return {
    hoge: ""
  }
},
methods: {
  関数 (hoge) {
    this.hoge = hoge;
  },
  
}
</script>
```
このように使うと子コンポーネントの値が親で使えます。

#まとめ
最後にややこしいけどめっちゃ重要なことをまとめておきます。
Vueの親子コンポーネント間でデータをやりとりするときの鉄則は Events Up, Props Down　です。
すなわち、

- 親 -> 子へデータを受け渡す際には``v-bind``と``props``を使う(props down)
- 子 -> 親へデータを受け渡す際には``$emit``と``v-on``を使う(events up)

#最後に
最後まで読んでいただきありがとうございました。
もし、間違い等、アドバイス、ご指摘等有れば教えていただけたら幸いです。