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>
というタグが使用可能になる。
- 第一引数には文字列を渡す(文字列を詰めた変数でももちろんok) 命名はハイフンで繋いだケバブケースが推奨されている
- 第二引数にはコンポーネントの構成情報を入れたオブジェクトが入る templateやprops、dataなどのオプションが使用できる elはルートVueにしか記述できない
- コンポーネントは何度でも使用可能。上記で言うと、
<list-item>
を複数書いたりできる コンポーネントを使用するたびに、あらたにコンポーネントインスタンスが生成される - コンポーネントの親と子 以下に示したように、コンポーネントの構成情報の中でコンポーネントを使用した場合、使用した側が親コンポーネントになる。 また、Vueインスタンスは全てのコンポーネントの親インスタンス(親コンポーネント)となる
//子コンポーネント 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.extend()を使ったコンポーネントの定義方法もあるが、基本的にはVue.component()で定義する。
グローバルとローカルコンポーネント Vue.component()を使ってVueインスタンスの外に定義するとグローバルなコンポーネントとなり、どのVueインスタンスでも使用できるかわりに、トラブルの温床となる。
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や描画関数などがあるが、この基本の書き方でも表現は可能。
コンポーネントごとにライフサイクルフックを設定可能
コンポーネントのデータは関数で定義する コンポーネントでdataをオブジェクトで定義すると、そのコンポーネントのインスタンス全てでdataが共有されてしまう。
たとえば、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を使うとこれをシンプルに解決できる。