【Vue.js】はじめてのVue.js〜概要＋インスタンス＋ディレクティブ〜

JavaScriptのフレームワークである「Vue.js」についての記事です。学習途中で至らない点等あると思うのですが、備忘録兼自分と同じ初学者の方向けにまとめてみました。

現在はVue.jsを用いてポートフォリオを書き換えつつ、学習を進めています。記述したコードも用いているので、是非参考にしてみてください。ポートフォリオは[こちら](https://portfolio-f4b3a.web.app/)、GitHubは[こちら](https://github.com/watanabedaigo/Portfolio)のページをご覧ください。ポートフォリオ内の学習スキルを記述した以下の部分と
<img width="500px" alt="代替テキスト" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1031318/e3a78077-15c4-1553-ed86-1d4980bcfb19.png">
ヘッダーのアイコン部分を
<img width="500px" alt="代替テキスト" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1031318/9dcdd05e-0dd4-8a22-7e56-33ef4abca42e.png">
の２つを例にインスタンス、ディレクティブの書き方をまとめています。

##目次
1. 概要
 [1. Vue.jsとは](#11-vuejsとは)
 [2. メリット](#12-メリット)
 [3. 環境準備](#13-環境準備)

2. インスタンス
 [1. インスタンスとは](#21-インスタンスとは)
 [2. 書き方](#22-書き方)
 [3. elオプションオブジェクト](#23-elオプションオブジェクト)
 [4. dataオプションオブジェクト](#24-dataオプションオブジェクト)
 [5. methodsオプションオブジェクト](#25-methodsオプションオブジェクト)
  
3. ディレクティブ
 [1. ディレクティブとは ](#31-ディレクティブとは )
 [2. v-bind](#32-v-bind)
 [3. v-on](#33- v-on)
 [4. v-html](#34-v-html)
 [5. v-for](#35-v-for)

[4. 最後に](#4-最後に)

##1. 概要
####1.1 Vue.jsとは
JavaScriptのフレームワーク[^1]の一種。DOM操作を自動化して効率良く、そしてコードを複雑にしすぎることなくシンプルにフロントエンドの開発が行うことができる。
Vue.jsという土台の上で、ライブラリを用いて必要な機能を追加していくことで、効率良く開発を進めることができる。

[^1]: フレームワークとは、Webアプリケーションやシステムを開発するのに必要な機能が予め用意された骨組みのこと。便利ではあるが、フレームワーク毎にルールが存在する。

####1.2 メリット
扱うデータやイベントが多くなっても、コードが複雑になりにくいことが挙げられる。理由は大きく分けて２つある。

①DOM操作を自動で行ってくれる
今回の記事で紹介するインスタンスやディレクティブを用いることで実現することができる。詳細は以下の「2.インスタンス」「3.ディレクティブ」を参照。

②DOM要素を部品化して使い回すことができる
こちらは次の記事で紹介するコンポーネントを用いることで実現することができる。

####1.3 環境準備
HTMLファイルにCDN[^2]を読み込む。至ってシンプル。HTMLのファイル名をindex.htmlとする。

```html:index.html
<!-- 開発バージョン、便利なコンソールの警告が含まれています -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
```
または

```html:index.html
<!-- 本番バージョン、サイズと速度のために最適化されています -->
<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
```
[^2]: CDNとは、「コンテンツデリバリーネットワーク」(Content Delivery Network) の略で、世界中に張り巡らされた配信ネットワークを利用して、Webサイトにアクセスしようとするユーザーに効率的かつ高速にWebコンテンツを配信する仕組み。<br>配信元のサーバー（オリジンサーバー）にエンドユーザーのリクエストが集中するとサーバーへの負担が大きくなってしまう。そこで、オリジンサーバーのウェブコンテンツのコピーを取得した複数のキャッシュサーバーでウェブコンテンツを配信することで、オリジンサーバーへの負担を軽減している。

##2. インスタンス
####2.1 インスタンスとは
一言で表すと、複製したオブジェクト[^3]のこと。
[^3]: データや処理のまとまり。配列との違いは、データや処理に名前（キー）を指定することができる点。特徴は「継承：変数を持たせてオブジェクトを複製することができる」、「多態性：インスタンス内でオブジェクトの内容を上書きすることができる」、「カプセル化：変数のアクセス権の指定」がある。

####2.2 書き方
new Vue({...}) によって、Vueオブジェクトのインスタンスを作る。今回はskillsというインスタンスを作成している。

```JavaScript:main.js
const skills = new Vue({
    el:'#skills',
    data:{
        skills:[
            {
                id:'frontend_skills',
            },
            //他のバックエンド、DataBase等も同様なので省略   
        ],
    },
})

const header = new Vue({
    el:'header',
    methods:{
        scroll:function(event){
            switch(event.currentTarget){
                case $('.about-icon')[0]:
                    const about = $('#about').offset().top;
                    $("html, body").animate({ scrollTop: about - 20 }, 200);
                    break;
                //他のcaseも同様なので省略
            }
        }
    }
})
```
newは演算子の一つ、Vue({...})は、オブジェクトを複製するための特別な関数(コンストラクタ)である。コンストラクタの引数には、オブジェクトを渡す。Vue.jsではこのオブジェクトを、オプションオブジェクトという。オプションオブジェクトに決められたプロパティを設定することで、Vue.jsの機能を利用することができる。

skillsインスタンスでは`elオプションオブジェクト`、`dataオプションオブジェクト`を、headerインスタンスでは`elオプションオブジェクト`、`methodsオプションオブジェクト`を使用している。

####2.3 elオプションオブジェクト
element（要素）の略称。DOM要素を設定する。
`#skills`では、id属性がskillsの要素を設定している。この要素内でのみインスタンスの中身を使用することができる。

####2.4 dataオプションオブジェクト
データを設定する。このデータにはVueインスタンスのプロパティとしてアクセスできる。

```html:index.html
<div id="skills">
  <p>{{ skills[0].id }}</p>
</div>
```
このように記述することで、インスタンスのdataオプションオブジェクト内のskills配列のインデックス番号０番目要素のidプロパティの値を表示することができる。
{{...}}をMustache構文といい、インスタンスのdataオプションオブジェクト内のデータとHTMLを結びつけることができる。これをデータバインディングという。
ただし、Mustache構文では値をテキストデータとして扱うため、プロパティの値に`<br>`や`<span>`要素を記述しても、文字列として表示されてしまう。HTMLへの変換は`v-htmlディレクティブ`を用いる必要がある。

####2.5 methodsオプションオブジェクト
elで設定したDOM要素内で関数を使用したい場合に用いる。`v-onディレクティブ`で指定することで関数を使用することができる。

##3. ディレクティブ
####3.1 ディレクティブとは
「2.4 dataオプションオブジェクト」で紹介したように、Mustache構文を用いることで、DOM要素とインスタンスのデータをバインディングすることができた。
しかし、DOM要素の属性とインスタンスのデータをバインディングする際にMustache構文は使うことができない。
そこで登場するのがディレクティブ。一言で表すと、Vue.jsで使用する特別な属性のこと。

####3.2 v-bind
上記の例で、インスタンスのdataオプションオブジェクト内のskills配列のインデックス番号０番目要素のidプロパティの値を表示するのではなく、`<p>`のid属性に設定したい場合は、以下のように記述する。

```html:index.html
<div id="skills">
  <p v-bind:id="skills[0].id">v-bindディレクティブ</p>
</div>
```
v-bind:の後に属性を指定し、属性値にインスタンスのdataオプションオブジェクトのプロパティを記述する。

動的なclass属性のバインディングは少し特殊なのでメモしておく。
`v-bind:class`にオブジェクトを渡すことでクラスを動的に切り替えることができる。その際、クラスの付与をデータプロパティの真偽性によって決めている。参考までに、ドキュメントの例を載せておく。
以下のような要素があったとする。`isActive`が`true`の場合は`active`クラスが付与され、`hasError`が`true`の場合は`text-denger`クラスが付与されることを表している。

```html
<div
  class="static"
  v-bind:class="{ active: isActive, 'text-danger': hasError }"
></div>
```
インスタンス内のdataオプションオブジェクトは以下のものだったとする。

```JavaScript
data: {
  isActive: true,
  hasError: false
}
```
すると、以下のように特定のクラスのみを付与することができる。

```html
<div class="static active"></div>
```


####3.3 v-on
「2.5 methodsオプションオブジェクト」で紹介したmethodsオプションオブジェクトをDOM要素内で使用する際に用いる。

```html:index.html
<header>
  <span v-on:click="scroll"><i class="fas fa-user-alt"></i></span>
  <!-- 他のアイコンも同様なので省略 -->
</header>
```

```JavaScript:main.js
const header = new Vue({
    el:'header',
    methods:{
        scroll:function(event){
            switch(event.currentTarget){
                case $('.about-icon')[0]:
                    const about = $('#about').offset().top;
                    $("html, body").animate({ scrollTop: about - 20 }, 200);
                    break;
                //他のcaseも同様なので省略
            }
        }
    }
})
```
methodsオプションオブジェクトを使用したいDOM要素に`v-onディレクティブ`を記述する。
`v-on:click="scroll"`のように、v-on:イベントの発生条件:"イベントハンドラ[^4]"の形で記述する。
[^4]: 指定したイベント(click,hover等)が発生した際にプログラムが実行する処理。

####3.4 v-html
「2.4 dataオプションオブジェクト」で紹介したように、Mustache構文を用いることでDOM要素とインスタンスのデータをバインディングすることができるが、値をテキストデータとして扱ってしまう。値に`<br>`や`<span>`を使用してDOM要素内で反映させたい場合、v-htmlディレクティブを用いる。

```JavaScript:main.js
const skills = new Vue({
    el:'#skills',
    data:{
        skills:[
            {
                show:'<strong>フロントエンド</strong>',
            },
            //他のバックエンド、DataBase等も同様なので省略   
        ],
    },
})
```
```html:index.html
<div id="skills">
  <p v-html="skills[0].show">v-htmlディレクティブ</p>
</div>
```
このように記述することで、インスタンスのdataオプションオブジェクト内のskills配列のインデックス番号０番目要素のshowプロパティの値を生のHTMLとして出力することができるため、`<strong>`が反映される。

####3.5 v-for
繰り返し処理を行うために用いる。
例えば、以下のskills配列のshowプロパティの値を全てDOM要素にバインディングして表示したいとする。

```JavaScript:main.js
const skills = new Vue({
    el:'#skills',
    data:{
        skills:[
            {
                id:'frontend_skills',
                show:'フロントエンド',
            },
            {
                id:'backend_skills',
                show:'バックエンド',
            },
            {
                id:'database_skills',
                show:'DataBase',
            },
            {
                id:'infrastructure_skills',
                show:'インフラ',
            },
            {
                id:'others',
                show:'その他',
            },
            {
                id:'stydying',
                show:'学習中',
            },
               
        ],
    },
})
```
「2.4 dataオプションオブジェクト」で紹介したように、Mustache構文を用いて以下のように記述することができる。

```html:index.html
<div id="skills">
  <p v-bind:id="skills[0].id">{{ skills[0].show }}</p>
  <p v-bind:id="skills[1].id">{{ skills[1].show }}</p>
  <p v-bind:id="skills[2].id">{{ skills[2].show }}</p>
  <p v-bind:id="skills[3].id">{{ skills[3].show }}</p>
  <p v-bind:id="skills[4].id">{{ skills[4].show }}</p>
  <p v-bind:id="skills[5].id">{{ skills[5].show }}</p>
</div>
```
しかし、変更が生じた場合、上記の書き方だと全ての要素を変えないといけないため非効率である。そこで`v-forディレクティブ`を用いる。以下のように書き換えることができる。

```html:index.html
<div id="skills">
  <p v-for="skill in skills" v-bind:id="skill.id">{{ skill.show }}</p>
</div>
```
`v-for="skill in skills"`で、skills配列の中身を取り出し、skillという変数に格納し、配列の要素の数だけ繰り返すことを表している。以降`skill.プロパティ名`でデータにアクセスしている。

##4. 最後に
ポートフォリオを修正する中で使用したディレクティブのみ紹介しています。今後他のディレクティブを使用した際は、再度記事を更新します。

調べて概要を理解してコードを書く。これを繰り返すことが理解の１番の近道だと思うので、今後も継続していきます。

来週はDOM要素を部品化をするための`コンポーネント`を紹介します。