Vue.jsの親子コンポーネントで親から子へデータを渡す(これは邪道でしょうか?)
Vue.jsで、親子関係のコンポーネントを作って親から子へデータを渡すってのはprops
で指定する方法とかいろいろ入門書に書かれている。
ただ、ローカルで定義した親コンポーネントの中に子コンポーネントを使っていて、親コンポーネントを使っているVueインスタンスのデータを、子コンポーネントまで渡す方法がよくわからない。
Vue.jsを勉強し始めたばかりなので自分が知らないだけと思う。 きっと本家マニュアルや詳しい書籍には書いてあるんだろうが、とりあえずゴニョゴニョ試行錯誤していたら出来てしまった。
なのでこれがまともな方法なのか全く分からないが、一応出来ているのでメモっておく。
Vueインスタンス側でローカルコンポーネントの関数を定義する(クロージャの利用)
まずHTML
<body> <div id="app"> <my-component></my-component> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script> <script src="main.js"></script> </body>
my-component
が親コンポーネント。この中に子コンポーネントが読み込まれる。
で、ここで読み込んでいるmain.js
は
/* 親子関係のコンポーネント 親から子へ属性(プロパティ)でデータを渡す。 */ // 子コンポーネント let child_comp = Vue.component('comp-child', { template: '<p id="child" class="child">{{val}}</p>', props: ['val'] }); // 親コンポーネント let myComponent = { template: `<div> <comp-child v-bind:val="message" class='parent'></comp-child> </div>` }; // Vueアプリ側 let app = new Vue({ el: '#app', data: { message: 'Message from Parent' }, beforeMount: function() { let msg = this.message; myComponent.data = function(){ return { message: msg }}; }, components: { 'my-component': myComponent } });
これでHTMLがこう出力される。
<body> <div id="app"> <div> <p id="child" class="child parent">Message from Parent</p> </div> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script> <script src="main.js"></script> </body>
Vueインスタンス側で、ローカルな親コンポーネントをcomponents: { 'my-component': myComponent }
とひも付けるのは同じ。
要するに本来コンポーネント側に定義すべき「データを返す関数」を、Vueインスタンス側でコンポーネントの外からクロージャ化された形で定義する。その中でVueインスタンスのdata
に定義された属性を読み込ませている。beforeMount
するタイミングでそれが読み込まれる仕組み。data
内のmessage
はmyComponent.data
のそばでローカル変数にいったん格納されなければ(つまりクロージャにしないと)機能しない。だから次のようなのはうまくいかない。
myComponent.data = function(){ return { message: this.message } };
コンポーネントの「データを返す関数」をVueインスタンス側で外から定義するなんてやり方は、入門的な本には書いていなかったように思う。読み落としているのかもしれないが。
なのでこんなやり方でいいかわからないが、これでVueインスタンス側のデータを書き換えると、ちゃんと更新された内容が親から子コンポーネントへと渡される。
もっと簡便な方法(普通にローカル変数を利用)
とはいえコンポーネントの関数をVueインスタンス側で外から定義するなんてのは、見た目の邪道感がぬぐえない。むしろこういう目的ならVueインスタンスやコンポーネントの外側(ファイル・ローカルな)オブジェクトを定義しておいて、それをコンポーネントやVueインスタンスで利用するほうが普通なのかも。
たとえばこんなふうに。
<body> <div id="app"> <p>{{ main_message }}</p> <my-component></my-component> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script> <script src="main20.js"></script> </body>
let child_comp = Vue.component('comp-child', { template: '<p id="child" class="child">{{val}}</p>', props: ['val'] }); let msg_data = { message: 'Message from Parent Comp!' }; let myComponent = { template: `<div> <p>in Parent Template -> {{ p_message }}</p> <comp-child v-bind:val="p_message" class='parent'></comp-child> </div>`, data: function() { return { p_message: msg_data.message } } }; let app = new Vue({ el: '#app', data: { main_message: msg_data.message }, components: { 'my-component': myComponent } });
<div id="app"> <p>Message from Parent Comp!</p> <div> <p>in Parent Template -> Message from Parent Comp!</p> <p id="child" class="child parent">Message from Parent Comp!</p> </div> </div>
Vueインスタンスやコンポーネントの外側でファイル・ローカルに定義したオブジェクト変数の中身は、Vueインスタンスからもコンポーネントからも(当たり前だが)利用できている。
ここでは同じローカルなデータをVueインスタンスとコンポーネントの両方からアクセスしたが、例えば、Vueインスタンス側のdata
はそのインスタンス間で共通利用するデータ、コンポーネント側のdata
はコンポーネント間で共通利用するデータとして、コンポーネントやVueインスタンスの記述外にローカルに定義ってのが、考え方としては分かりやすく、見た目も変なことしないで済むかもしれない。