前置き
前回に引き続き、Vue.jsの公式チュートリアルをゆっくり読んでいきます。
前回は主にv-bind等のディレクティブに触れました。今回は、「コンポーネント」についてです。
コンポーネントとは
コンポーネントとは一般的には「部品」のことですが、 チュートリアル上では、以下のように記載されています。
Vue においては、「コンポーネント」は本質的にはあらかじめ定義されたオプションを持つ Vue インスタンスです。
ところで「Vueインスタンス」とは何だったでしょうか。VueインスタンスはVue関数で作成されるインスタンスで、ここまでで何度も下記コードにより生成してきました。
new Vue({ /* オプション */ });
これは、ルートVueインスタンスと呼ばれるものを生成しており、全てのVueアプリケーションは、このルートVueインスタンスと、それにぶら下がるVueインスタンスによって構成されます。このルートVueインスタンスにぶら下がるVueインスタンスが「コンポーネント」にあたります。
コンポーネントの作成
早速コンポーネントを作成してみる前に、今までの知識でただメッセージを表示するだけのものを作成します。
初コンポーネントの前に
<div id="app1">
<div>{{ message }}</div>
</div>
<script>
let content1 = "[app1] Let's enjoy Vue!";
new Vue({
el: '#app1',
data: { message: content1 },
});
</script>
さきほどの話に当てはめると、new Vue({el: '#app1'})
で紐付けたid="app1"
のdiv要素がルートVueインスタンスに相当する要素と言えるでしょう。今度はメッセージ表示部分を、そのルートVueインスタンスにぶら下がるコンポーネントで作成してみます。
初コンポーネント(メッセージを表示するコンポーネント)
<div id="app2">
<message></message>
</div>
<script>
// message と呼ばれる新しいコンポーネントを定義
Vue.component('message', {
template: "<div>[app2] Let's enjoy Vue!</div>",
})
new Vue({
el: '#app2',
});
</script>
コンポーネントは、Vue.component()
を使って作成できます。第一引数にコンポーネントのタグ名、第二引数にオプションオブジェクトを指定します。オプションオブジェクトのtemplate
オプションでコンポーネント内に表示するhtmlを指定します。ここではメッセージを表示するmessageコンポーネントを作成しました。
これでid="app2"
の要素配下で、messageコンポーネントを使用してメッセージを表示することができました。
これでもいいのですが、コンポーネントは部品です。部品は他でも使えるように汎用的なものでなければいけません。上記の例では、messageコンポーネントのtemplate
オプションにメッセージを直書きしてしまっているので汎用性があるとは言えませんね。この部分を修正してみます。
コンポーネント(メッセージをdataに持たせて表示するコンポーネント)
<div id="app3">
<message></message>
</div>
<script>
let content3 = "[app3] Let's enjoy Vue!";
Vue.component('message', {
template: '<div>{{ content }}</div>',
data: function() { return { content: content3 }},
})
new Vue({
el: '#app3',
});
</script>
コンポーネントのオプションオブジェクトのdata
オプションにデータオブジェクトを持たせることで、template内の{{ }}
でデータを展開することができます。
ところで、data
オプションには下記のような直接データオブジェクトを持たせるのはでなく、データオブジェクトを返す無名関数を指定しています。
new Vue({
el: '#app3',
data: { content: content3 }, // ←コンポーネントではこれはNG
});
そもそもVue.component
に上記の形式でdataを指定すると警告メッセージが出るのですが、Vueのコンポーネントにおいて、これはNGとされており、チュートリアル内でも明言されています。
コンポーネントの data オプションは関数でなければなりません。各インスタンスが返されるデータオブジェクトの独立したコピーを保持できるためです:
なぜ関数で返すと、独立したコピーを保持できるのかはわかりません。。。(誰か教えてくださいm(_ _)m)(※)
※追記
こちらに関して@kanさんがコメントくださいました。ぜひコメント欄もご確認ください。
また、関数にすることはメモリ節約の意図もあるようです。→Understanding Vue.js Component Instancing - Alligator.io
情報くださり、ありがとうございます!
さて、これでメッセージをコンポーネント内に直書きすることは回避されましたが、例えばmessageコンポーネントを複製して個々に表示するメッセージを変えたい場合はどうでしょうか。スクリプト部分も複製しないといけませんね。これでは汎用性が高いとは言えません。
<message>
タグに<message content="Let's enjoy Vue!">
な感じに表示させたいメッセージを受け取るカスタム属性が指定できると良さそうです。
コンポーネント(メッセージをpropsに渡して表示するコンポーネント)
これを実現するには、messageコンポーネントにprops
オプションを指定します。ここにカスタム属性として指定したい属性名を配列で渡してあげます。
<div id="app4">
<message content="[app4] Let's enjoy Vue!"></message>
<message content="[app4] Hello Vue world!"></message>
</div>
<script>
Vue.component('message', {
template: '<div>{{ content }}</div>',
props: ['content'],
})
new Vue({
el: '#app4',
});
</script>
props
オプションを使用することでmessageコンポーネントにカスタム属性経由で個別のメッセージを渡して表示することができるようになりました。
今回はここまでです。