まとめ
- コンポーネントはJS&HTMLをセットで管理できる
- 親から子は属性で渡してporpsで受け取る
- 子から親は子が$emitでカスタムイベントで発火して親が受け取る
- スロットはテンプレートがカスタマイズできる
- コンポーネント開発を楽にしたいなら単一ファイルコンポーネントがおすすめ!
1. コンポーネントの定義
コンポーネントの登録
html
<div id="app">
<my-component></my-component>
<div>
Vue.js
Vue.component('my-component', {
template: '<p>MyComponent</p>',
//データはオブジェクトを返す関数にしなければならない
data: function() {
return { message: 'Hello!World' }
},
methods: {
}
})
ローカルへの登録(new Vueする前にコンポーネントを定義する)
Vue.js
var myComponent = {
template: '<p>MyComponent</p>'
}
new Vue({
el: '#app',
components: {
'my-component': myComponent
}
})
2. コンポーネント間の通信
コンポーネント間の通信(親から子)(props down)
プロパティとして文字列を渡す
html
<comp-child val="これは子A"></comp-child>
<comp-child val="これは子B"></comp-child>
Vue.js
Vue.component('comp-child', {
//テンプレートで受け取ったvalを使用
template: '<p>{{ val }}</p>',
//受け取る属性名を指定
props: ['val']
})
プロパティとしてデータを渡す
html
<comp-child :val="valueA"></comp-child>
<comp-child :val="valueB"></comp-child>
Vue.js
new Vue({
data: {
valueA: 'これは子A',
valueB: 'これは子B'
}
})
コンポーネントをリストレンダリング
子コンポーネント
html
<ul>
<comp-child v-for="item in list"
v-bind:key="item.id"
v-bind:name="item.name"
v-bind:hp="item.hp"></comp-child>
</ul>
Vue.js
Vue.component('comp-child', {
template: '<li>{{ name }} HP.{{ hp }}</li>',
props: ['name', 'hp']
})
親コンポーネント
Vue.js
new Vue({
el: '#app',
data: {
list: [
{ id: 1, name: 'スライム', hp: 100 },
{ id: 2, name: 'ゴブリン', hp: 200 },
{ id: 3, name: 'ドラゴン', hp: 500 }
]
}
})
エラーになるパターン
Vue.js
Vue.component('comp-child', {
template: '<li>{{ name }} HP.{{ hp }}
<button v-on:click="doAttack">攻撃する</button></li>',
props: ['name', 'hp'],
methods: {
doAttack: function () {
//propsで受け取ったデータは勝手に書き換えてはいけない!
this.hp -= 10 //-> [Vue warn] error!
}
}
})
propsの受け取りデータ型を指定する
Vue.js
//タイプチェックを省略した場合
Vue.component('example', {
props: ['value'] //どんな型も受け入れる
})
//データ型のみチェックする場合
Vue.component('example', {
props: {
value: データ型
}
})
インスタンスのチェック
html
<example v-bind:value="value"></example>
Vue.js
function Cat(name) {
this.name = name
}
Vue.component('example', {
props: {
value: Cat //猫データのみ許可!
}
})
new Vue({
data: {
value: new Cat('たま') //valueは猫データ
}
})
その他のオプションも使用する場合
Vue.js
Vue.component('example', {
props: {
value: {
type: [String, Number], //許可するデータ型
default: 100, //デフォルト値
required: true, //必須にする
validator: function (value) { //カスタムバリデータ関数、チェックして真偽値を返す
return value > 10
}
}
}
})
コンポーネント間の通信(子から親)(event up)
子のイベントを親にキャッチさせる
子コンポーネント
Vue.js
Vue.component('comp-child', {
//クリックでhandleClickを実行
template: '<button v-on:click="handleClick">イベント発火</button>',
methods: {
//ボタンのクリックイベントのハンドラでchilds-eventを発火する
handleClick: function () {
this.$emit('childs-event')
}
}
})
親コンポーネント
html
<comp-child v-on:childs-event="parentsMethod"></comp-child>
Vue.js
new Vue({
el: '#app',
methods: {
//childs-eventが発生した!
parentsMethod: function () {
alert('イベントをキャッチ! ')
}
}
})
親が持つデータを操作
子コンポーネント
Vue.js
Vue.component('comp-child', {
template: '<li>{{ name }} HP.{{ hp }}
<button v-on:click="doAttack">攻撃する</button></li>',
props: {
id: Number,
name: String,
hp: Number
},
methods: {
//ボタンのクリックイベントのハンドラから$emitでattackを発火する
doAttack: function () {
//引数として自分のIDを渡す
this.$emit('attack', this.id)
}
}
})
親コンポーネント
html
<ul>
<comp-child v-for="item in list"
v-bind:key="item.id"
v-bind="item"
v-on:attack="handleAttack"></comp-child>
</ul>
Vue.js
new Vue({
el: '#app',
data: {
list: [
{ id: 1, name: 'スライム', hp: 100 },
{ id: 2, name: 'ゴブリン', hp: 200 },
{ id: 3, name: 'ドラゴン', hp: 500 }
]
},
methods: {
//attackが発生した!
handleAttack: function (id) {
//引数のIDから要素を検索
var item = this.list.find(function (el) {
return el.id === id
})
//HPが0より多ければ10減らす
if (item !== undefined && item.hp > 0) item.hp -= 10
}
}
})
カスタムタグのイベントハンドリング
html
<!-- コンポーネント側からclickを$emitで呼び出さない限り発火しない -->
<my-icon v-on:click="handleClick"></my-icon>
<!-- .native修飾子を使い、元々のイベントを直接発火する -->
<my-icon v-on:click.native="handleClick"></my-icon>
コンポーネント間の通信(非親子)
Vue.js
//イベントバス用インスタンスを作成
var bus = new Vue({
data: {
count: 0
}
})
Vue.component('component-b', {
template: '<p>bus: {{ bus.count }}</p>',
computed: {
//busのデータを算出プロパティに使用
bus: function () {
return bus.$data
}
},
//createdフックでbus-eventが起きた時の処理をかく
created: function () {
bus.$on('bus-event', function () {
this.count++
})
}
})
コンポーネント間の通信(その他)
子コンポーネントを参照する$refs
親コンポーネント
html
<comp-child ref="child">
Vue.js
new Vue({
el: '#app',
methods: {
handleClick: function () {
//子コンポーネントのイベントを発火
this.$refs.child.$emit('open')
}
}
})
子コンポーネント
Vue.js
Vue.component('comp-child', {
template: '<div>...</div>',
created: function () {
//自分自身のイベント($onを使ってハンドルできる)
this.$on('open', function () {
console.log('なにか処理')
})
}
})
コンポーネントの属性のスコープ
コンポーネントの属性の値に当たる部分は、親のスコープになる
html
<comp-child v-on:childs-events="親のメソッド">
<comp-child v-on:childs-events="parentsMethod(親のデータ)">
子コンポーネントが引数を持って\$emitを実行している場合、
子コンポーネントの引数は\$event変数で使用できる
html
<comp-child v-on:childs-events="parentsMethod($event, parentsData)">
Vue.js
new Vue({
el: '#app',
data: {
parentsData: '親のデータ'
},
methods: {
parentsMethod: function(childsArg, parentsArg){
//処理
}
}
})
\$event変数は\$emitの第1引数しか持たない
→複数の引数を渡したい場合は、1つのオブジェクトにまとめる
Vue.js
this.$emit('childs-event', { id: 1 name: '新しい名前'})