【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">
を例にコンポーネントの書き方をまとめています。

インスタンス、ディレクティブに関する記事は[こちら](https://qiita.com/Q5khZxoivNcpFLu/items/6a70f0e4d5eb5cbfa5c0)をご覧ください。

##目次
1. コンポーネント<br>
 [1. コンポーネントとは](#11-コンポーネントとは)<br>
 [2. メリット](#12-メリット)<br>
 [3. 書き方](#13-書き方)<br>
 [4. Vue.component](#14-vuecomponent)<br>
 [5. templete](#15-templete)<br>
 [6. props](#16-props)<br>

##1. コンポーネント
####1.1 コンポーネントとは
再利用することのできる部品。
本記事のはじめに載せたポートフォリオ内のskillsの項目のように、大枠のフォーマットを踏襲しつつ、中身だけ変えて使い回すことができる。

####1.2 メリット
* HTMLファイルのコードが簡潔になり分かりやすい。
* 大枠のDOM要素の変更が生じても、jsファイルの一箇所を直せば全てに適応されるので管理が楽。

jsファイルに以下のようなインスタンスが定義してあり、インスタンスのdataオプションオブジェクト内のskills配列の中身を全て表示したいとする。

```javascript:main.js
const skills = new Vue({
    el:'#skills',
    data:{
        skills:[
            {
                id:'frontend_skills',
                show:'フロントエンド',
                icon:true,
                details:[
                    'HTML',
                    'CSS',
                    'JavaScript',
                    'jQuery',
                ],
            },
            {
                id:'backend_skills',
                show:'バックエンド',
                icon2:true,
                details:[
                    'php(Laravel)',
                ],
            },
            {
                id:'database_skills',
                show:'DataBase',
                icon3:true,
                details:[
                    'MySQL',
                    'Realtime<br>Database',
                ],
            },
            {
                id:'infrastructure_skills',
                show:'インフラ',
                icon4:true,
                details:[
                    'AWS(EC2,S3)',
                ],
            },
            {
                id:'others',
                show:'その他',
                icon5:true,
                details:[
                    'git',
                    'GitHub',
                    'firebase',
                    'Heroku',
                    'Bootstrap',
                ],
            },
            {
                id:'studying',
                show:'学習中',
                icon6:true,
                details:[
                    'Sass',
                    'Vue.js',
                    'React',
                ],
            },
        ],
    },
})
```
一つの手として、以下のようにHTMLファイルに直接書き込む方法がある。
見ての通り、６つの項目が続くためとても長い。また、DOM要素の変更が生じた際、６箇所直す必要がありミスが生じやすい。項目が６つだからなんとかなるが、項目の数がもっと多い場合は大変である・・・

```html:index.html
<section id="skills">
    <div>
        <h5><i class="fas fa-pen"></i>Skills<i class="fas fa-pen"></i></h5>
    </div>

    <div id="frontend_skills">
        <h6><i class="fab fa-html5"></i>フロントエンド</h6>
        <div>
            <ul>
                <li>HTML</li>
                <li>CSS</li>
                <li>JavaScript</li>
                <li>jQuery</li>
            </ul>
        </div>
    </div>

    <div id="backend_skills">
        <h6><i class="fab fa-html5"></i>バックエンド</h6>
        <div>
            <ul>
                <li>php(Laravel)</li>
            </ul>
        </div>
    </div>

    <div id="database_skills">
        <h6><i class="fab fa-html5"></i>DataBase</h6>
        <div>
            <ul>
                <li>MySQL</li>
                <li>Realtime<br>Database</li>
            </ul>
        </div>
    </div>

    <div id="infrastructure_skills">
        <h6><i class="fab fa-html5"></i>インフラ</h6>
        <div>
            <ul>
                <li>AWS(EC2,S3)</li>
            </ul>
        </div>
    </div>

    <div id="others">
        <h6><i class="fab fa-html5"></i>その他</h6>
        <div>
            <ul>
                <li>git</li>
                <li>GitHub</li>
                <li>firebase</li>
                <li>Heroku</li>
                <li>Bootstrap</li>
            </ul>
        </div>
    </div>

    <div id="studying">
        <h6><i class="fab fa-html5"></i>学習中</h6>
        <div>
            <ul>
                <li>Sass</li>
                <li>Vue.js</li>
                <li>React</li>
           </ul>
        </div>
    </div>
</section>
```
そこで登場するのがコンポーネント。実際に使用するとHTMLファイルを以下のように書き換えることができる。見ての通り、とてもシンプル！！！

```html:index.html
<section id="skills">
    <div>
        <h5><i class="fas fa-pen"></i>Skills<i class="fas fa-pen"></i></h5>
    </div>

    <skills-component
        v-for="skill in skills"
        v-bind:key="skill.id"
        v-bind:skill="skill"
    ></skills-component>
</section>
```
####1.3 書き方
```javascript:main.js
Vue.component('skills-component',{
    props:['skill'],
    template:`
        <div v-bind:id="skill.id">
            <h6><i v-bind:class="{'fab fa-html5':skill.icon,'fab fa-php':skill.icon2,'fas fa-database':skill.icon3,'fas fa-network-wired':skill.icon4,'fab fa-github-square':skill.icon5,'fas fa-pen':skill.icon6}"></i>{{ skill.show }}</h6>
            <div>
                <ul class="skills">
                    <li v-for="detail in skill.details" v-html="detail"></li>
                </ul>
            </div>
        </div>
    `
})
```
今回のコードに出てきた`Vue.component`、`props`、`template`の３つの説明をしつつ、書き方を整理していく。

####1.4 Vue.component
```javascript:main.js
Vue.component('skills-component',{
    props:[''],
    template:'',
})
```
Vue.componentメソッドを呼び出すことでコンポーネントを作成することができる。

第１引数には、コンポーネントにつける名前を渡す。もとから用意されてるHTML要素の名前とかぶるのを防ぐために、複数の単語を組み合わせて名前をつける。今回はskillsを表示するコンポーネントと言うことで、`skill-component`という名前にしている。
第２引数には、コンポーネントの内容などをオプションとして渡す。今回は　`props`、`templete`を渡している。

これにより、skills-componentというオリジナルの要素をHTMLファイルで使用することができる。

####1.5 templete
コンポーネントの中身を決めるもの。

中身は単一ルートである必要があるため、以下のような記述だとエラーが出る。解決策としては、全てを`div要素`で囲んで一つのDOM要素にしてしまう。

```javascript:main.js
Vue.component('skills-component',{
    //propsは省略
    template:`
        <h6><i v-bind:class="{'fab fa-html5':skill.icon,'fab fa-php':skill.icon2,'fas fa-database':skill.icon3,'fas fa-network-wired':skill.icon4,'fab fa-github-square':skill.icon5,'fas fa-pen':skill.icon6}"></i>{{ skill.show }}</h6>
        <div>
            <ul class="skills">
                <li v-for="detail in skill.details" v-html="detail"></li>
            </ul>
        </div>
    `
})
```
このコンポーネントを、インスタンスのdataオプションオブジェクト内のskills配列の中身全てに使用するために、コンポーネント自体に`v-forディレクティブ`を用いている。今回は配列から取り出したものをskillという変数に格納し、繰り返し処理をしている。

```html:index.html
<section id="skills">
    <skills-component
        v-for="skill in skills"
    ></skills-component>
</section>
```
では、繰り返し処理をする中で、それぞれ異なった値を表示させるためにはどのようにすればいい？？？
そこで登場するのがpropsオプジョン。

####1.6 props
コンポーネントに値を渡す架け橋になるもの。

以下の場合、コンポーネントにインスタンスのdataオプションオブジェクト内のskills配列の要素のid,show,detailsプロパティを渡している。

今回はskills配列から取り出したものをskillという変数に格納し、繰り返し処理をしている。skillの持つプロパティを全て渡す場合、以下のようにプロパティを`props:['skill']`として書くこともできる。「skillの変数が持つ全てのプロパティを渡しますよ〜」ということを表している。
渡すプロパティが多くなるとコードが長くなってしまうため、こちらの書き方の方が良い。

```javascript:main.js
Vue.component('skills-component',{  
    //一つずつプロパティを書く方法
    props:[
        'id',
        'show',
        'details',
    ],

    //全てのプロパティを一気に書く方法
    props:['skill'],

    //templeteは省略
```
そして、以下のように`カスタム属性(propsで指定したプロパティのこと)`を通してプロパティの値を受け取らせる。

```html:index.html
<section id="skills">
    <!-- 一つずつプロパティを書く方法 -->
    <skills-component
        v-for="skill in skills"
        v-bind:id="skill.id"
        v-bind:show="skill.show"
        v-bind:details="skill.details"
    ></skills-component>

    <!-- 全てのプロパティを一気に書く方法 -->
    <skills-component
        v-for="skill in skills"
        v-bind:skill="skill"
    ></skills-component>
</section>
```
以上で、コンポーネントのテンプレートにプロパティを渡すことができるため、繰り返し処理の中で、その都度異なった値を表示することができる。