基礎からメモ: Vue.js CH5-p146 コンポーネントとは

コンポーネントとは、機能を持つUI部品ごとにテンプレートとJavaScriptを1つのセットにして、開発管理するための仕組み。

コンポーネントの定義方法

コンポーネントを定義(グローバル)

Vue.component('my-component', {
  template: '<p>My Component</p>'
});

HTML側でこのコンポーネントを使用

<div id="app">
  <my-component></my-component>
</div>

表示結果

<div id="app"><p>My Component</p></div>

コンポーネントを定義(ローカル)

コンポーネントを定義したオブジェクトを作り、特定のコンポーネント(ルートコンストラクタ=メインのVueインスタンス定義もコンポーネント)のcomponentsオプションに登録することで、その親コンポーネントのスコープ内だけで使用できるローカルコンポーネントになる。ただし子になるコンポーネントは、それをオプションに登録する親コンポーネントよりも先に定義しておく必要がある。

let myComponent = {
  template: '<p>My Component</p>'
};

/* 
componentオプションに登録されたコンポーネントは、
この特定のローカルコンポーネントのスコープ内だけで使える.
この場合、my-component は#app内でのみ有効。
*/

let app = new Vue({
  el: '#app',
  data: {
    //.........................
  },
  methods: {
    //.........................
  },
  components: {
    'my-component': myComponent
  }
});

定義部分のカスタムタグ名はキャメルケースやパスカルケースも使えるが、DOMとして認識されるテンプレート内では<my-component>のようなケバブケースで書かなければならない点に注意。 アイコンなど汎用性の高いテンプレートでなければ、このローカルな登録方法のほうがカスタムタグ名を汚染しないのでおすすめ。

コンポーネントのオプション

コンポーネントの中には、new Vue()のオプションと同じように、 データやメソッドも定義できる。

ただし、dataはオブジェクトを返す関数であることの違いがある。これは複数のコンポーネントインスタンス間で同じオブジェクトを参照しないようにするため。

Vue.component('my-component', {
  template: '<p>{{message}}</p>',
  data: function() {
    return {
      message: 'Hello Vue.js'
    }
  },
  methods: {
    // ルートコンストラクタと同じようにメソッド、算出プロパティ、ウォッチャなどを定義できる
  }
});

コンポーネントのローカルな定義

コンポーネントの中にデータやメソッドも定義できるので、componentsオプションも使える。なので、(利用価値がどこまであるかどうかはわからないが)子コンポーネントをローカルに定義することもできる。

/*
親子関係のコンポーネント
子コンポーネントもローカルで定義
*/

const compChild = {
  template: '<p>My Local Child Component</p>'
};

const myComponent = {
  template: '<div><comp-child></comp-child></div>',
  components: { 'comp-child': compChild }
};

let app = new Vue({
  el: '#app',
  components: {
    'my-component': myComponent
  }
});

ただ、子コンポーネントのテンプレートなどが肥大化し、複雑になると単一ファイルコンポーネント.vue)という仕組みを使うようになるので、それについては後ほど。

テンプレートは単一のルート要素に囲まれていなければならない

/* 
テンプレートのルート要素は単一であること。
次のコードはエラーになる。
Error: Component template should contain exactly one root element
*/
Vue.component('my-component', {
  template: '<span>Hello</span><span>{{message}}!</span>',
});
/* 
テンプレートのルート要素は単一であること。
エラーを避けるにはテンプレートの全体を単一の要素で囲む。
*/
Vue.component('my-component', {
  template: '<div><span>Hello</span><span> {{message}}!</span></div>',
});

コンポーネントインスタンス

同じコンポーネントを複数回使うと、別々のインスタンスとして扱われる。 次の例では、Countをボタンクリックで増やすと別々に増える。

<div id="app">
  <my-component></my-component>
  <my-component></my-component>
</div>
let myComponent = {
  template: `<p>
    <span>Hello</span>
    <span> {{message}}!</span>
    <span> Count: {{count}}!</span><br><br>
    <button v-on:click="increment">カウント+1</button>
  </p>`,
  data: function() {
    return {
      message: 'Custom Tag',
      count: 0
    }
  },
  methods: {
    increment: function() {
      this.count += 1;
    }
  }
};

let app = new Vue({
  el: '#app',  
  components: {
    'my-component': myComponent
  }
});

ここまでの単純な例では、コンポーネントで利用するデータやメソッドは、コンポーネント側で定義しておく必要がある。 ルートインスタンスも含めた親インスタンス側のデータが、子コンポーネントで呼び込まれるわけではない。

親子間でデータを受け渡すには、次のコンポーネント間の通信方法を使わないといけない。