はじめに
第3回では、
Vue.
Vue.jsのコンポーネント指向
Vue.jsのコンポーネント指向について
Vue.
大規模なアプリケーションを作成する際は、
基本的なcomponent
まず、
Vue.component('fruits-list-title', {
template: '<h1>フルーツ一覧</h1>'
})
この例では、<h1>フルーツ一覧</h1>
のHTML要素を含んだコンポーネントを、fruits-list-title
という名前で登録しています。登録したコンポーネントを別のコンポーネントから使用するには、
<div id="fruits-list">
<fruits-list-title></fruits-list-title>
</div>
このように構成した場合、
<div id="fruits-list">
<h1>フルーツ一覧</h1>
</div>
上記の例ではグローバルのVueにコンポーネントを、Vue.
の形式で追加していましたが、Vue.
を使用してコンポーネントを作成した上で、components
というoptionの中に以下のように登録してください。
var fruitsListChild = Vue.extend({
template: '<h1>フルーツ一覧</h1>'
})
var fruitsListParent = Vue.extend({
template: '<div>親コンポーネント<fruits-list-child></fruits-list-child></div>',
components: {
'fruits-list-child': fruitsListChild
}
})
new Vue({
el: "#fruits-list",
components: {
'fruits-list-parent': fruitsListParent
}
})
<div id="fruits-list">
<fruits-list-parent></fruits-list-parent>
</div>
jsfiddleで実行するとわかりますが、fruits-list-child
コンポーネントはfruits-list-parent
コンポーネントのスコープ内に定義されています。そのため、fruits-list-child
コンポーネントをfruits-list-parent
の外側で指定しても、
<div id="fruits-list">
<fruits-list-child></fruits-list-child>
<fruits-list-parent></fruits-list-parent>
</div>
コンポーネントのdataの扱い
各インスタンスごとに異なるdataオブジェクトを定義したいとき、
data: function(){
return {
fruits: [/* */]
}
}
コンポーネント間の通信
親コンポーネントと子コンポーネントのデータのやりとりを解説します。
![図1 コンポーネント間の通信図 図1 コンポーネント間の通信図](/assets/images/dev/serial/01/vuejs/0003/thumb/TH800_001.jpg)
親コンポーネントが子コンポーネントへデータを渡す際には、
例を見ていきましょう。以下のように、fruitsItems
に入ったフルーツの名前fruitsItem.
をリストするテンプレートを含んだコンポーネントを、fruits-list
という名前でVueインスタンスに登録しています。
Vue.component('fruits-list', {
props: ['fruitsItem'],
template: '<li>{{fruitsItem.name}}</li>'
});
配列fruitsは親コンポーネントのdataで定義します。
new Vue({
el: '#fruits-component',
data: {
fruitsItems: [
{name: '梨'},
{name: 'イチゴ'}
]
}
});
上記のように子コンポーネントのpropsオプションに変数名fruitsItems
を追加したうえで、fruits-list
タグの属性にfruits-items
を記述します。すると、
propsにキャメルケースでfruitsItems
と書いた場合、fruits-items
と書きます。
<div id="fruits-component">
<ol>
<fruits-list v-for="fruit in fruitsItems" fruits-item="fruit"></fruits-list>
</ol>
</div>
ここで、v-bind
ディレクティブを使用すると良いでしょう。
<fruits-list v-for="fruit in fruitsItems" v-bind:fruits-item="fruit"></fruits-list>
このように記述すると、fruits
の値が更新されるたびに、props
に書いたfruitsItems
が更新されます。なお、v-bind
は省略できます。
<fruits-list v-for="fruit in fruitsItems" :fruits-items="fruits"></fruits-list>
レンダリングされるHTML要素は以下のようになります。
<div id="fruits-component">
<ol>
<li>梨</li>
<li>イチゴ</li>
</ol>
</div>
ここまでのサンプルコードは、
子コンポーネントから親コンポーネントへの通信では、
- イベントのlisten:
$on(eventName)
- イベントのtrigger:
$emit(eventName)
※ $dispatchや$broadcastを紹介する記事を読んだことのある方もいるかもしれませんが、
こちらも例を見ていきましょう。以下のようにcounter-button
コンポーネントが定義されているとします。ボタンを押すとこのコンポーネントのaddToCart
メソッドが呼ばれ、increment
というカスタムイベントが発行されます。親コンポーネント側では v-on:increment(increment)
でincrement
イベントをlistenしているため、increment
メソッドが呼ばれます。
var counterButton = Vue.component( {
template: '<span>{{counter}}個<button v-on:click="addToCart">追加</button></span>',
data: function () {
return {
counter: 0
}
},
methods: {
addToCart: function () {
this.counter += 1
this.$emit('increment')
}
},
});
new Vue({
el: '#fruits-counter',
components:
'counter-button': counterButton
},
data: {
total: 0,
fruits: [
{name: '梨'},
{name: 'イチゴ'}
]
},
methods: {
increment: function () {
this.total += 1
}
}
});
<div id="fruits-counter">
<div v-for="fruit in fruits">
{{fruit.name}}: <counter-button v-on:increment="increment()"></counter-button>
</div>
<p>合計: {{total}}</p>
</div>
ここまでのサンプルコードは、
大規模なアプリケーションを作成していると、
コンポーネントの作成
ログインフォームコンポーネントの作成
ここまでの知識を踏まえてログインフォームを作成しましょう。まずtemplateはログインIDとパスワードのinputフォームを用意し、v-model
でコンポーネントのデータとバインドしておきます。ボタンにはv-on:click(login)
と記述し、
<div id="login-template">
<input type="text" placeholder="ログインID" v-model="userid">
<input type="password" placeholder="パスワード" v-model="password">
<button v-on:click="login">ログイン</button>
</div>
コンポーネントのdataにはuser_
とpassword
を返す関数を定義し、login
メソッドを定義しています。なお、auth.
という仮の関数を置いています。
var auth = {
login: function(id, pass){
window.alert("login id:" + id + "\n" + "password:" + pass);
}
}
var loginTemplate = `
<div>
<input type="text" placeholder="ログインID" v-model="userid">
<input type="password" placeholder="パスワード" v-model="password">
<button v-on:click="login">ログイン</button>
</div>
`
Vue.component('user-login', {
template: loginTemplate,
data: function(){
return {
userid: '',
password: ''
}
},
methods: {
login: function(){
auth.login(this.userid, this.password);
}
}
});
以上で簡単なログイン用のコンポーネントが作成できました。このコンポーネントを、
new Vue({
el: "#login-example"
});
<div id="login-example">
<user-login></user-login>
</div>
ここまでのサンプルコードは、
ヘッダーコンポーネントの作成
より実践的な例として、
まずは、page
というコンポーネントの中に、
<page>
<page-header></page-header>
<!-- ページのコンテンツ -->
</page>
ここで、content distribution
という仕組みを使用します。Vue.content distribution
のAPIは、
たとえば、page-header
コンポーネントを定義するとします。
var headerTemplate = `
<div style="background: white;">
<slot name="header"></slot>
</div>
`
Vue.component('page-header', {
template: headerTemplate
});
上記のようにヘッダーのコンポーネントの中にslot
というタグを埋め込みます。こうすることで親コンポーネントを使用する際に、
<div>
<page-header>
<h1 slot="header">冬の果物</h1>
</page-header>
<ul>
<li>りんご</li>
<li>イチゴ</li>
</ul>
</div>
上記のように、slot
属性に対応する子コンポーネントのslotタグのname
属性を指定することで、
<div style="background: white;">
<h1>冬の果物</h1>
</div>
<li>りんご</li>
<li>イチゴ</li>
</ul>
</div>
ここまでのサンプルコードは、
まとめ
いかがでしたか。ここまでVue.
次回は、