Vue.js、JavaScript フレームワークを初めて使う方に向けて、今回はキモとなる Component について解説します。
Component の概念から説明していますので、初心者の方にも読みやすい内容です。
結論
- Component は使いまわし可能なパーツで、メンテナンス性と再利用性を高める
- 親子間のデータ受け渡しは Binding で実現する
- 単方向バインディング (
v-bind/v-on) と双方向バインディング (v-bind+v-on/sync) を使い分ける
Component とは?
Vue.js や Angular には Component という概念があります。
Component を使用して Vue.js などはコードの共通化を高めています。共通化を行うとメンテナンス性の向上やコード量の削減ができます。
どんなプロジェクトでも必ず Component は使用するので、Vue.js を理解するのには必須です。
使いまわし可能なパーツ
Component は簡単に説明すると使いまわし可能なパーツです。
vue ファイルの中で他の vue ファイルを呼び出して使用することができます。
例えばボタンを使いまわし可能な状態で実装すれば、呼び出すだけで同じデザイン、同じ処理を呼び出し側で実装できます。
例:ボタン
実現したいこと: 幅と高さが 20px の「ここをクリック」と表示されたボタンを使いまわしたい。
実装方法:
- A ファイルで幅と高さが 20px の「ここをクリック」と表示されたボタンを作成。
- B ファイルでボタン Component を呼び出す。
結果: B ファイルでボタンが表示される。
この呼ばれた側 (A ファイル)、呼び出し側 (B ファイル) は親と子で呼ぶことが多いです。
以下からは
- 呼び出し側 = 親
- 呼ばれた側 = 子
で説明していきます。
Binding とは?
上記の説明だと、ボタンの場合は次のような不満点があります。
- ボタンのテキストが固定
- クリック後の処理を子で書く必要がある
Binding はその不満点を解消する機能です。
Component 間のデータ受け渡し機能
Binding は簡単に説明すると Component 間のデータ受け渡し機能です。
Binding を行うと
- 親で子のテキストを変更可能にする
- 子でイベントトリガーを親に引き継ぐ
などができます。結果的に上記で起きていた不満点は、Binding を行うと解決します。
これらの Binding は親と子の関係性が重要で、
- 親から子、子から親の一方通行な Binding は一般的に単方向バインディング
- 親と子の双方向な Binding は一般的に双方向バインディング
と呼びます。
Binding の概念が理解できないと Component は上手く扱えないので頑張りましょう。
実際の使用例
ここからは実際に Vue.js のコードを見ながら実装方法を解説します。
vue ファイルの基本については以下の記事で解説していますので、わからない方は参考にしてください。
Component の使用
まずはベースとなる Component の呼び出しを解説します。
上記の例の Binding をしていないボタンの実装方法です。
子で string 文字列を表示する vue ファイルを作成し、親で子を読みます。
子: ChildComponent.vue
<template>
string
</template>
子では処理不要なので、文字を書いているだけです。
親: ParentComponent.vue
<template>
<ChildComponent />
</template>
<script>
import ChildComponent from "xxxx/ChildComponent.vue";
export default {
components: { ChildComponent },
};
</script>
親では子の vue ファイル、ChildComponent.vue を使用するために ChildComponent.vue を import します。
そのうえで、html 側で実際に使用する箇所に <ChildComponent /> と記述すれば OK です。
v-bind (単方向バインディング: 親 → 子)
Component の呼び出し方法がわかったので、次に親 → 子の Binding を説明します。
親で子のテキストを変更可能にするイメージです。
以下のコードは親で設定したテキストを子で表示します。
子: ChildComponent.vue
<template>
{{bindName}}
</template>
<script>
export default {
props: ["bindName"],
};
</script>
子では親からのデータを受け取るため、props を用意します。
受け取るデータの名前は親と子で連携させる必要があります。今回は bindName と名付けます。
props は配列型なので、親から受け取るデータを複数持つこともできます。
親: ParentComponent.vue
<template>
<ChildComponent :bindName="dataName" />
</template>
<script>
import ChildComponent from "xxxx/ChildComponent.vue";
export default {
components: { ChildComponent },
data() {
return {
dataName: 'string'
};
},
};
</script>
親では html 側で呼び出します。呼び出す時に :bindName="dataName" と属性が設定されています。
ここで親と子のデータをバインド、連携させています。
bindNameは子の props で設定した名前dataNameは親で扱っているデータの名前
これで、親 → 子の Binding は完了です。
v-on (単方向バインディング: 子 → 親)
次に、子 → 親の Binding を説明します。
子で発生したイベントを親に検知、引き継ぐイメージです。
以下のコードは子のテキストをクリックすると親でイベントを検知して、親で表示されているテキスト内容が string → モジ に変更します。
子: ChildComponent.vue
<template>
<div>
<div @click="clickText()">
クリックして変更
</div>
</div>
</template>
<script>
export default {
methods: {
clickText() {
this.$emit('onName', 'モジ');
}
}
};
</script>
子ではクリックイベントを設定します。@click がクリックイベント設定です。
script 側では対象の Function 内で emit と記述しています。これが、子から親にイベントを検知させる方法です。
- 第一引数の onName は検知させたい名前
- 第二引数の モジ は実際に送るデータ (オブジェクト型でも OK)
これで「クリックして変更」をクリックすると親の onName に伝播します。
親: ParentComponent.vue
<template>
{{dataName}}
<ChildComponent @onName="changeName"/>
</template>
<script>
import ChildComponent from "xxxx/ChildComponent.vue";
export default {
components: { ChildComponent },
data() {
return {
dataName: 'string'
};
},
methods: {
changeName(item) {
this.dataName = item
}
}
};
</script>
親では基本的に v-bind と同様のことをしています。
他の追加処理はテキストの変更処理です。methods 内の changeName 引数に emit で設定した値が梱包されています。
これで、子 → 親の Binding は完了です。
応用編
今までは単方向バインディングのみだったので、双方向にデータを受け渡すことができません。
そこで以下からは、双方向バインディングの実装方法を解説します。
双方向バインディングが最も難しいので頑張りましょう。
v-bind と v-on (双方向バインディング)
Vue.js の双方向バインディングは大きく 2 種類の方法があります。
最初に最もベーシックな方法を解説します。
上記で解説した
- v-bind (単方向バインディング: 親 → 子)
- v-on (単方向バインディング: 子 → 親)
を組み合わせる方法です。
以下のコードの概要です。
- 親で子で表示する文字列を設定 (string)
- 子でクリックすると親にクリックイベントを送る (モジと書かれたデータを送信)
- 親で文字列が string → モジ に変更
- 子の string がモジに変更
先程までは子、親の順に書きましたが、理解しやすさ優先でここからは親、子の順に書きます。
親: ParentComponent.vue
<template>
<ChildComponent :bindName="dataName" @onName="changeName"/>
</template>
<script>
import ChildComponent from "xxxx/ChildComponent.vue";
export default {
components: { ChildComponent },
data() {
return {
dataName: 'string'
};
},
methods: {
changeName(item) {
this.dataName = item
}
}
};
</script>
行っていることは v-bind と v-on をやっているだけです。親はそこそこシンプルです。
子: ChildComponent.vue
<template>
<div>
{{bindName}}
<div @click="clickText()">
クリックして変更
</div>
</div>
</template>
<script>
export default {
props: ["bindName"],
methods: {
clickText() {
this.$emit('onName', 'モジ');
}
}
};
</script>
子も同様に v-bind と v-on をやっているだけです。
分かりづらい場合は、上で書いた順序通りに処理を追うと理解しやすいです。
sync (双方向バインディング)
v-bind と v-on で双方向バインディングすると処理が少し複雑になります。
そこで、超応用の sync の登場です。
sync は簡単に説明すると v-bind を同期処理に変換します。
以下のコードでも先程と同じような処理を行っています。
- 親で子で表示する文字列を設定 (string)
- 子でクリックすると親にクリックイベントを送る (モジと書かれたデータを送信)
- 親で文字列が string → モジ に変更
- 子の string がモジに変更
親: ParentComponent.vue
<template>
<ChildComponent :bindName.sync="dataName" />
</template>
<script>
import ChildComponent from "xxxx/ChildComponent.vue";
export default {
components: { ChildComponent },
data() {
return {
dataName: 'string'
};
},
};
</script>
親の処理がスッキリしましたね。
ポイントは bindName.sync です。sync を書くことで v-on で行っていた代入処理を省略できます。
子: ChildComponent.vue
<template>
<div>
{{bindName}}
<div @click="clickText()">
クリックして変更
</div>
</div>
</template>
<script>
export default {
props: ["bindName"],
methods: {
clickText() {
this.$emit('update:bindName', 'モジ');
}
}
};
</script>
子も少し変化しています。emit の第一引数に update が追加されています。
update を書くことで、親の sync に通知がいく仕組みです。
v-on と sync の違い
紹介したとおり sync だと処理を省略して書けるので便利ですが、注意点もあります。
sync は同期処理なので、フィルター処理をはさみたい時などデータを加工したい時は使用できません。
双方向バインディングは以下の使い分けをおすすめします。
- vue ファイル間でデータを加工するなら v-bind と v-on
- vue ファイル間でデータを加工しないなら sync
まとめ
今回は Vue.js の Component と Binding を紹介しました。
とても重要な機能なので、何回も読み直してマネぶことをおすすめします。
関連記事: