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内のmessagemyComponent.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インスタンスの記述外にローカルに定義ってのが、考え方としては分かりやすく、見た目も変なことしないで済むかもしれない。