0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Vue.jsのコンポーネントの基本を勉強する

Last updated at Posted at 2019-07-22

コンポーネントの基本

コンポーネントは名前付きの再利用可能なVueインスタンスです。
コンポーネントをnew Vueで作成されたルートVueインスタンス内でカスタム要素として使用できる。
自分だけの要素を作れると考えてください。

<div id="example">
  <button-counter></button-counter>
</div>
// Vueコンポーネント作成
Vue.component(
  'button-counter',
  {
    data() {
      return {
        count:0
      }
    },
    template: '<button @click="count++">カウントアップ:{{ count }}</button>'
  }
)

// Vueインスタンス作成
let app = new Vue({
  el: '#example',
})

上記の例ではbutton-counterという要素名(コンポーネント名)を登録し、その下括弧({})で括られた部分はVue.jsの処理を記述しています。
templateオプションには名前の通りテンプレート構文を記述します。

Vue.componentはVueインスタンスなので同様のライフサイクルフックなどのオプションを使用できますが、例外としてelのようなルート固有のオプションは使用できません。

new Vueで作成したインスタンスのルートVueはidがexampleの要素ですので、この要素の内でコンポーネント要素を記述します。

コンポーネントの再利用

<div id="example">
  <button-counter></button-counter>
  <button-counter></button-counter>
  <button-counter></button-counter>
</div>

これらの要素はそれぞれが別々のインスタンスとして作成されるのでcountなどのプロパティも独自で保持します。

dataは関数でなければならない

各インスタンスがdataプロパティの独立性を保つためです。

Vueインスタンスの場合

data:{
  count:0
},

Vueコンポーネントの場合

data() {
  return {
    count:0
  }
},

// 別の書き方
data: function () {
  return {
    count: 0
  }
}

コンポーネントの編成

components.png

例えば、ヘッダー、サイドバー、およびコンテンツ領域のコンポーネントがあり、それぞれには一般にナビゲーションリンク、ブログ投稿などの他のコンポーネントが含まれます。

コンポーネントをテンプレート構文内で使用するにはVue.jsがそれらを認識できるように登録する必要があります。
コンポーネント登録には、グローバルローカルの二種類があります。
これまでの例ではVue.componentを使用してコンポーネントをグローバルに登録していました。

Vue.component(
  'button-counter',
  {
    // コンポーネントの内容
  }
)

グローバルに登録されたコンポーネントは、その後に作成されたルートVueインスタンス(new Vue)のテンプレー構文内で使用できます。
さらに、そのVueインスタンスのコンポーネントツリーの全てのサブコンポーネント内でも使用できる。

プロパティを使用した子コンポーネントへのデータの受け渡し

特定のデータをコンポーネントに渡すことができない限り、そのコンポーネントは役に立ちません。
プロパティ(props)はここで役に立ちます。

プロパティ(props)はコンポーネントに登録できるカスタム属性です。
値が渡されることでそのコンポーネントインスタンスのプロパティになります。
propsオプションを使用して、コンポーネントが受け入れるプロパティのリストに登録することができる。

<div id="example">
  <message-div message= "コンポーネントインスタンス1" ></message-div>
  <message-div message= "コンポーネントインスタンス2" ></message-div>
  <message-div message= "コンポーネントインスタンス3" ></message-div>
</div>
Vue.component(
  'message-div',
  {
    props: ['message'],
    template: '<p>{{ message }}</p>'
  }
)

let app = new Vue({
  el: '#example',
})

通常のアプリケーションでは配列などで可変的にインスタンスを生成したいことがあると思います。

v-forを使って動的にプロパティを渡しコンポーネントのインスタンスを生成することができます。

<div id="example">
  <post-message
    v-for="post in posts"
    :key="post.id"
    :message="post.message"
  ></post-message>
</div>
Vue.component(
  'post-message',
  {
    props: ['message'],
    template: '<p>{{ message }}</p>'
  }
)

let app = new Vue({
  el: '#example',
  data: {
    posts: [
      {id:1, message: 'コンポーネントインスタンス1'},
      {id:2, message: 'コンポーネントインスタンス2'},
      {id:3, message: 'コンポーネントインスタンス3'}
    ]
  }
})

単一のルート要素

コンポーネントを構築するとき、テンプレート構文には最終的に1要素以上のものが欲しくなります。
そういうときは親要素をつくり子要素として複数の要素を記述します。
またそれぞれの情報ごとにプロパティを定義してしまうと、とてもごちゃごちゃすると思います。
そういう時は単一のプロパティ(配列やオブジェクトのdataプロパティ)

<div id="example">
  <post-message
    v-for="post in posts"
    :key="post.id"
    :post="post"
  ></post-message>
</div>
Vue.component(
  'post-message',
  {
    props: ['post'],
    template: '<div><p>メッセージID: {{ post.id }}</p><p>メッセージ:{{ post.message }}</p></div>'
  }
)

let app = new Vue({
  el: '#example',
  data: {
    posts: [
      {id:1, message: 'コンポーネントインスタンス1'},
      {id:2, message: 'コンポーネントインスタンス2'},
      {id:3, message: 'コンポーネントインスタンス3'}
    ]
  }
})

上記の例では新しいプロパティがpostオブジェクトに追加される際にはいつでも、< post-message > 内で自動的に利用可能になるのです。

子コンポーネントのイベントを購読する

コンポーネントを開発する際、親コンポーネントとやろとりする機能が必要になるかもしれません。
親コンポーネントはv-onを使って子コンポーネントで起きた任意のイベントを購買することができます。

<div id="example" :style="{ fontSize: postFontSize + 'em' }">
  <p>親要素</p>
  <post-message
    v-for="post in posts"
    :key="post.id"
    :post="post"
    @size="postFontSize += 0.1"
  ></post-message>
</div>
Vue.component(
  'post-message',
  {
    props: ['post'],
    template: '\
      <div>\
        <p>{{ post.title }}</p><p>メッセージ:{{ post.message }}</p>\
        <button @click="$emit(\'size\')">コンポーネント全ての文字拡大</button>\
      </div>\
    '
  }
)

let app = new Vue({
  el: '#example',
  data: {
    postFontSize: 1,
    posts: [
      {id:1,title:'子要素', message: 'コンポーネント'}
    ]
  }
})

上記の例では、子コンポーネントでページ全体のフォントを大きくするボタンを用意し、そのボタンが押下されたとき、< div id="example" :style="{ fontSize: postFontSize + 'em' }" >要素の中全体が大きくなるようにします。
v-onで渡したメソッド(@size)を子コンポーネントは$emitメソッドで受け取りメソッドを実行することができます。

イベントと値を送出する

イベントを特定の値付きで送出すると便利なことがあります。
上記の例では親がフォントの拡大率を決めていましたが、子コンポーネントが$emitの2番目のパラメータを使って値を送出することができる。
親コンポーネントでイベントを起動を確認すると、送出された$emitの2番目のパラメータは $eventでアクセスできます。

<div id="example" :style="{ fontSize: postFontSize + 'em' }">
  <p>親要素</p>
  <post-message
    v-for="post in posts"
    :key="post.id"
    :post="post"
    @size="postFontSize += $event"
  ></post-message>
</div>
Vue.component(
  'post-message',
  {
    props: ['post'],
    template: '\
      <div>\
        <p>{{ post.title }}</p><p>メッセージ:{{ post.message }}</p>\
        <button @click="$emit(\'size\', 0.1)">コンポーネント全ての文字拡大</button>\
      </div>\
    '
  }
)

let app = new Vue({
  el: '#example',
  data: {
    postFontSize: 1,
    posts: [
      {id:1,title:'子要素', message: 'コンポーネント'}
    ]
  }
})

イベントハンドラがメソッドの場合、送出された$emitの2番目のパラメータはそのメソッドの最初のパラメータとして渡されます。

<div id="example" :style="{ fontSize: postFontSize + 'em' }">
  <p>親要素</p>
  <post-message
    v-for="post in posts"
    :key="post.id"
    :post="post"
    @size="sizeUp"
  ></post-message>
</div>
Vue.component(
  'post-message',
  {
    props: ['post'],
    template: `
      <div>
        <p>{{ post.title }}</p><p>メッセージ:{{ post.message }}</p>
        <button @click="$emit(\'size\', 0.1)">コンポーネント全ての文字拡大</button>
      </div>
    `
  }
)

let app = new Vue({
  el: '#example',
  data: {
    postFontSize: 1,
    posts: [
      {id:1,title:'子要素', message: 'コンポーネント'}
    ]
  },
  methods: {
    sizeUp: function(event){
      this.postFontSize += event
    }
  },
})

コンポーネントでv-modelを使う

カスタムイベントv-modelで動作するカスタム入力を作成することもできます。
コンポーネント内の < input > は下記のようでなければなりません。

  • value属性をvalueプロパティにバインドする
  • inputでは、新しい値で独自のカスタムinputイベントを送出する
<div id="example">
  <p>入力フォーム</p>
  <custom-input v-model="searchText"></custom-input>
  <p>{{ searchText }}</p>
</div>
Vue.component('custom-input', {
  props: ['value'],
  template: `
    <input
      v-bind:value="value"
      v-on:input="$emit('input', $event.target.value)"
    >
  `
})

let app = new Vue({
  el: '#example',
  data() {
    return {
      searchText: ''
    }
  },
})

スロットによるコンテンツ配信

コンポーネントにコンテンツを渡すことができます。

スロットガイド

<div id="example">
  <p>入力フォーム</p>
  <custom-message
    :title="title"
  >
    <custom-input v-model="searchText"></custom-input>
  </custom-message>
</div>
Vue.component('custom-message', {
  props: ['title'],
  template: `
    <div>
      <p> {{ title }} </p>
      <slot></slot>
    </div>
  `
})

Vue.component('custom-input', {
  props: ['value'],
  template: `
    <input
      v-bind:value="value"
      v-on:input="$emit('input', $event.target.value)"
    >
  `
})

let app = new Vue({
  el: '#example',
  data() {
    return {
      title: '子要素',
      searchText: ''
    }
  },
})

上記の例では< custom-message >コンポーネントの中で< custom-input >コンポーネントを記述しています。
< custom-input >コンポーネントは< custom-message >コンポーネントのslot要素に渡されます。
あたかも< custom-message >コンポーネントが< custom-input >コンポーネントを呼び出しているように見えますが、宣言しているのは親である**app**ですので、< custom-input >コンポーネントのイベントや値のやりとりは親と行われます。

動的なコンポーネント

コンポーネントを動的に切り替えることができる。
Vueの< component >要素と 特別な属性のisで実装可能です。

<div id="example">
  <component :is="componentId"></component>
  <button @click="conversion('custom-message1')">コンポーネント切り替え1</button>
  <button @click="conversion('custom-message2')">コンポーネント切り替え2</button>
  <button @click="conversion('custom-message3')">コンポーネント切り替え3</button>
</div>
Vue.component('custom-message1', {
  template: `
    <div>
      <p> コンポーネント1 </p>
    </div>
  `
})

Vue.component('custom-message2', {
  template: `
    <div>
      <p> コンポーネント2 </p>
    </div>
  `
})

Vue.component('custom-message3', {
  template: `
    <div>
      <p> コンポーネント3 </p>
    </div>
  `
})


let app = new Vue({
  el: '#example',
  data() {
    return {
      componentId: 'custom-message1'
    }
  },
  methods: {
    conversion:function(params) {
      this.componentId = params
    }
  },
})

上記の例ではisで指定したcomponentIdがそれのボタンによってconversionメソッドに引数をコンポーネントとして呼び出しconversionメソッドはcomponentIdに押下されたボタンに対応したコンポーネントが入り、コンポーネントが切り替わります。

あとがき

今回で一区切りなので一旦細かく書く記事は終了したいと思います。
記載しなかった題目

DOM テンプレートパース時の警告

参考資料

Vue公式リファレンス

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?