Vue.jsのコンポーネントについて

はじめに

Vue.jsのコンポーネントについて学習した。その忘備録。

Vue.js入門 基礎から実践アプリケーション開発まで | 川口 和也, 喜多 啓介, 野田 陽平, 手島 拓也, 片山 真也 | コンピュータ・IT | Kindleストア | Amazon

Vueコンポーネント

Vue.jsは、 1. 小さく 2. 自己完結し 3. (多くの場合)再利用可能

という特徴を持ったコンポーネント を組み合わせてアプリケーションを設計する思想を持っている。

Vue.jsにおけるコンポーネントとは、再利用可能なVueインスタンスのこと。

how to

基本

Vue.component()の第一引数にコンポーネント名、第二引数にコンポーネントの内容を記述してコンポーネントを作成する。

Vue.component('list-item', {
    template: '<li>foo</li>'
    })

とすると、ルートVueで指定したDOM内で、<list-item>というタグが使用可能になる。

//子コンポーネント
Vue.component('fruits-list-title', {
    template: '<h1>フルーツ一覧</h1>'
    })

//親コンポーネント
Vue.component('fruits-list', {
    template: '<div><fruits-list-title></fruits-list-title></div>'
    })
    
//Vueは正確にはコンポーネントではないが、親コンポーネントと呼ぶこともある
new Vue({el: '#main'})

Vueインスタンスの中でcomponentオプションを使って定義をすると、「そのVueインスタンスがelで指定した箇所」でのみ使えるローカルなコンポーネントとなる。

new Vue({
        el: "fruits-list",
        components: {
            'fruits-list-title': {
            template: '<h1>フルーツ一覧</h1>'
            }
        }
    })

とすると

<div id="fruits-list">
    <div>
        <fruits-list-title></fruits-list-title>
    </div>
</div>

といった感じで使える。

-もっと複雑なコンポーネントを作成したいときに便利な仕組みとして、text/x-templateや描画関数などがあるが、この基本の書き方でも表現は可能。

たとえば、fooというコンポーネントでcountというdataをオブジェクトで定義し、<foo>タグを複数箇所に用いた場合、countに10といれると、<foo>すべてのインスタンスのcountが10になってしまう。

これを防ぎ、コンポーネントインスタンスごとにdataを定義するには、dataを、オブジェクトを返す関数で定義する必要がある。

コンポーネント間の通信

基本概要

Vue.jsでは各コンポーネントはそれぞれ独立しており、基本的に外部からデータを参照したり、コンポーネント間でデータのやりとりはできない。

しかし、実際には外部からデータを参照したり、コンポーネント間でデータのやりとりをしないと実現できない処理は多い(たとえば、ラインナップが変化する商品の一覧を表示するコンポーネントなどは、ラインナップのデータを参照する必要がある)

とはいえ、野放図に各コンポーネントがデータを参照できるようでは設計が混乱する。

そこで、

「Vue.jsでは、親コンポーネントからのみ、子コンポーネントへデータを渡すことが可能とする」

というルールがある。逆に、子コンポーネント内のデータを親コンポーネントで参照はできない。

親データを子が参照 → OK!

子データを親が参照 → NG!

コンポーネントから子コンポーネントへデータの伝播

propsオプションを使用する。

Vue.component(コンポーネント名, {
    props: {
        親から受け取る属性名:{
            type: StringやObjectなどのデータ型,
            default: デフォルト値、
            required: 必須かどうかの真偽値,
            validator: バリデーション用の関数
        }
    }
    //このようにpropsオプションを設定することでtemplate内で「親から受け取る属性」が使えるようになる
})

propsで設定した親から受け取るデータは、v-bind経由で渡す。

<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

//v-bindに渡す属性名はケバブケースで記述
<div id="app">
    <item-desc v-bind:item-name='myItem'></item-desc>
</div>

<script>
//親コンポーネントであるVueのdataを参照するためのprops記述
Vue.component('item-desc' ,{
   props: {
       itemName: {
           type: String
       }
   }, 
   template: '<p>{{ itemName }}は便利です。</p>'
})

new Vue({
   el: '#app',
   data: { myItem: 'pen'}
})

Vuexで解決

通常、直接的な親子関係にないVueコンポーネント間では直接的には情報のやりとりがおこなえず、構造が複雑になるが、Vuexを使うとこれをシンプルに解決できる。