Vue.jsを触って早数週間経過します。
最も理解に時間がかかったコンポーネントの連携についてまとめてみます。
理解が怪しいところはマサカリをお願いいたします。
ゴール
子コンポーネントから親コンポーネントの関数を呼び出す
環境
- Node.js 7.7.1
- npm 4.4.2
- Vue.js 2.2.4
propsでデータを渡す
props
とは「親コンポーネントからデータを受け取るためにエクスポートされた属性のリスト/ハッシュ 」です。
Vue.js > API > props
https://jp.vuejs.org/v2/api/#props
最もシンプルな例を見てみましょう。
呼び出し元
<template>
<child-component foo="bar" />
</template>
具体的には、次の定義です。
これは今まで慣れ親しんできたHTML
と同じ形式ですね。
<[コンポーネント名] [属性]="[値]">
子コンポーネント
<script>
export default {
props: [
'foo'
]
}
</script>
子コンポーネントにpropsを定義しておけば、コンポーネント呼び出し時に与えられた属性、値にアクセスすることができます。
<template>
<p>{{ foo }}</p><!-- bar -->
</template>
HTMLでは受け取った値をこのように出力することができます。
props + v-bindでデータを渡す
上記はリテラルを子コンポーネントに渡す方法でした。
次にv-bindを利用して親コンポーネントのデータを子コンポーネントに渡してみましょう。
Vue.js > API > v-bind
https://jp.vuejs.org/v2/api/#v-bind
呼び出し元
<script>
export default {
data() {
return {
var: "hoge"
}
}
}
</script>
<template>
<child-component v-bind:foo="var" />
</template>
変数を定義し、子コンポーネントに与えます。
初めの例と異なるのは属性名fooの前にv-bind
が付いているかどうかだけですね。
子コンポーネント
<script>
export default {
props: [
'foo'
]
}
</script>
<template>
<p>{{ foo }}</p> <!-- hoge -->
</template>
子コンポーネントは初めのサンプルと全く同一です。
しかしながらprops経由で受け取ったfooの中身ははリテラルではななく、親で定義されたオブジェクトのため、fooの値としてhogeが出力されます。
つまり、どういうことだってばよ
Vue.jsでは v-から始まる属性をディレクティブ(Directive)
と呼び、特別な属性 として扱います。
ディレクティブとは、 DOM 要素に対して何かを実行することをライブラリに伝達する、マークアップ中の特別なトークンです。
http://012-jp.vuejs.org/guide/directives.html
次はVue.jsにプレーンなHTMLとして解釈されます。
<child-component foo="bar" />
次はv- で始まるディレクティブですね。
<child-component v-bind:foo="var" />
v-bindは「どの属性にどの式を与えるか(束縛するか)を指示するもの」で、噛み砕くとここでは「属性fooに式varを適用する」という指示を行なっています。
噛み砕くと、次の形です。
<[コンポーネント名] v-bind:[bindする属性]="[bindする式]">
bindされた式はリアクティブ
で、つまり元のデータであるvarの値を書き換えると、属性値にも即反映される実装が可能です。
ちなみにv-bindは次のように省略して記述することができます。
<child-component :foo="var" />
プレーンなHTMLと並べてみると、、、コロン(:)があるかどうか だけですね。
何この綺麗すぎる実装!!
<child-component foo="bar" />
<child-component :foo="var" />
子コンポーネントから親のメソッドを呼び出す
さて、ようやくゴールとなる課題に取り掛かりましょう。
親コンポーネントのメソッドを、子コンポーネントから引数付きで呼び出します。
ちょっと無理やり感がありますが、次のようなサンプルを用意しました。
親となるコンポーネントから子にパラメーターを渡し、子は受け取ったパラメーターによって処理を分けるというイメージです。
親コンポーネント
<script>
export default {
methods: {
parentFunc(foo, bar) {
console.log(foo)
console.log(bar)
}
}
}
</script>
<template>
<div>
ボタンA<child-component v-on:call-parent="parentFunc" foo="1" bar="A" />
ボタンB<child-component v-on:call-parent="parentFunc" foo="2" bar="B" />
</div>
</template>
親コンポーネントからは子へは、次の形でv-on
を用いてイベント名を知らせます。
v-on:[イベント名]="[親コンポーネントメソッド]"
子コンポーネント
<script>
export default {
props: [
'foo',
'bar'
],
methods: {
childFunc(foo, bar) {
this.$emit('call-parent', foo, bar)
},
},
}
</script>
<template>
<button v-on:click="childFunc(foo, bar)" type="button">Button</button>
</template>
親から子へ渡ってきたのはメソッドではなく、イベント名でしたね。
子コンポーネントから親のメソッドを実行するためには、子から$emit
でイベントを発火させるのがポイントです。
templateから直接イベントを発生させることはできませんため、クリックイベントを一旦 子コンポーネントのメソッド(ここではchildFunc)で受け取り、そのメソッド内でイベントを処理します。
ボタンをクリックすると、引数に応じてconsole.logの値がしっかり出し分けられていることを確認できますよ。
わかるのですが、回りくどい実装ではありますよねー。
親子の連携は特殊なルールで成り立っているようで、ぜひVue.jsの次をガイドもご覧ください。
Vue.js > ガイド > カスタムイベントとの-v-on-の使用
https://jp.vuejs.org/v2/guide/components.html#カスタムイベントとの-v-on-の使用
おわりに
Vue.js
は学習コストが低く、使いやすいライブラリです。
現在 1プロジェクトをVue.jsで開発中ですが、要件をこなしていくとつまづきポイントや弱点がわかってきますねー。
引き続き勉強したことを記事にできればと思っています。
是非みなさんもモダンなJSライブラリで楽しい開発ライフを過ごしてください。
この記事はtomita@atuwebがお届けしました。