コンポーネント
Vue.jsはWeb Componentsのコンセプトを取り入れたコンポーネントの仕組みを持っています。コンポーネントはデータの定義とDOMの構築などを含めた部品を作成することができる仕組みです。親子コンポーネント間でイベントを通じたデータのやりとりも可能です。
Polymerなどに代表されるようなPolyfillへの外部依存はなく、Vue.extend()を使ってコンポーネントを作成することができます。Vue.extend()を使用するときに渡すオプションに関しては、前回の記事で解説したVMインスタンスを生成する際のオプションとほとんど同じです。
ただし、elとdataオプションに関して、再利用され複製される用途が主であるコンポーネントでは、インスタンスごとに初期化した値を渡すためにfunctionとして定義する点が異なります。
コンポーネントのライフサイクルに関しては、前回の記事で紹介したVMインスタンスのライフサイクルと同じです。
Vue.extend()でコンポーネントの必要なオプションを記述して、実際にカスタムエレメントとして登録するにはVue.component()を呼びます。以下、dataに与えた値を表示するだけの簡単なコンポーネントの例です。
<div id="app"> <!-- v-componentを使用する方法 --> <div v-component="my-component"></div> <!-- カスタムエレメントとして使用する方法 --> <my-component></my-component> </div> <script> var MyComponent = Vue.extend({ template: 'A custom component {{title}}', data: function(){ return {title: 'カスタムエレメント'} } }); // コンポーネントを登録 Vue.component('my-component', MyComponent); var vm = new Vue({ el: '#app' }); </script>
上記のコードを実行すると”A custom component カスタムエレメント”という文字列がv-componentによる記述と、カスタムエレメント形式の記述で2つ表示されます。
Vue.extend()を省略した簡易的な記述も可能です。Vue.component()の第2引数にVue.extend()へのオプションを直接セットします。
// Vue.extendを省略して以下のように記述することもできる Vue.component('my-component', { template: 'A custom component {{title}}!', data: function(){ return {title: 'カスタムエレメント'} } });
親VMインスタンスの中で自身の配下にコンポーネントを直接登録することもできます。その場合は、Vue.compoent()を使ったグローバルな登録は必要なく、親VMインスタンス生成時のcomponentsオプションとして指定します。
W3Cが提唱するカスタムエレメントの仕様として、タグには必ずハイフン(-)を1つ以上含める必要があります。Vue.jsのコンポーネントもWeb Componentsの仕様に準拠するためにv-componentを使わずにカスタムのタグ名で記述する場合はハイフンを含める必要があります。
また、カスタムエレメントはブラウザにとって未知のタグであるため、div要素にv-componentを指定する時とは異なりデフォルトではブロック要素として解釈されないので注意が必要です。
コンポーネントへのデータの受け渡し
Vue.component()で登録されたコンポーネントはカスタムフィルターやカスタムディレクティブと同様にグローバルに登録され、デフォルトでは親スコープのデータとは独立しています。明示的にコンポーネントへデータを渡したい場合はv-withを使うことができます。また、配列に対してはv-repeatを使うことができます。
v-withを使用したデータの受け渡し
v-withを使用して親から子コンポーネントへ直接オブジェクトを指定する例を紹介します。親Vueインスタンスの$dataのオブジェクトuserを引数なしに指定すると、子コンポーネントの$dataとしてセットされます。
<div id="app"> <p v-component="user-profile" v-with="user"></p> </div> <script> // コンポーネントを登録する$dataに対応するオブジェクトはv-withで親から与えられる Vue.component('user-profile', { template: '{{name}}<br>{{email}}' }); var parent = new Vue({ el: '#app', data: { user: { name: 'Foo Bar', email: 'foo@bar.com' } } }); </script>
v-withで子コンポーネントにデータを渡す際に、引数にプロパティを指定することができます。v-with="childProp: parentProp"の形式で渡すと、parent[parentProp]とchild[childProp]が双方向でバインディングされます(v0.11.5時点)。
双方向のバインディングの例を示すために、以下ではコンポーネントが受け取った値をクリック時に変更しています。クリック時の値の変更によりparent側のプロパティ値も動的に変更されます。
<div id="app"> <input v-model="parentMsg"> <p v-component="child" v-with="childMsg : parentMsg"></p> </div> <script> var parent = new Vue({ el: '#app', data: { parentMsg: 'Inherited message' }, components: { child: { template: '<span v-on="click: update">{{childMsg}}</span>', methods: { update: function(){ // childMsgを更新すると動的に親のparentMsgも変更される this.childMsg = "Updated message by child"; } } } } }); </script>
v-repeatを使用したデータの受け渡し
v-repeatを用いてコンポーネントにオブジェクトの配列を渡すことができます。配列のそれぞれのオブジェクトに対してViewModelが生成され、コンポーネントのデータとしてセットされます。
<div id="app"> <ul> <li v-repeat="users" v-component="user-profile"></li> </ul> </div> <script> var parent = new Vue({ el: '#app', data: { users: [ { name: 'Chuck Norris', email: 'chuck@norris.com' }, { name: 'Bruce Lee', email: 'bruce@lee.com' } ] }, components: { 'user-profile': { template: '{{name}}<br>{{email}}', } } }); </script>
![図3. v-repeatを使用したデータの受け渡し実行例](http://cz-cdn.shoeisha.jp/static/images/article/8567/8567_01a.gif)
v-refによる子コンポーネントの参照
親VMインスタンスから子コンポーネントへアクセスが必要な場合は以下のようにv-refをセットすることで参照することができます。
<div id="parent"> <div v-component="user-profile" v-ref="profile"></div> </div> <script> Vue.component('user-profile', { template: 'This is a user profile' }); var parent = new Vue({ el: '#parent' }); // 子コンポーネントへv-refで指定した値でアクセスできる var child = parent.$.profile; </script>