ryota21silvaの技術ブログ

Funna(ふんな)の技術ブログ

これまで学んだ技術の備忘録。未来の自分が救われることを信じて

Vueのコンポーネント(追記していく)

はじめに

今日もとりあえずVueの内容について殴り書きしていく。

componentとは

Webサイトの画面はヘッダー、メイン、フッターなど色々な部品から構成されている。そこで、コンポーネントという機能を使えば、ヘッダーなどの部品(コンポーネント)ごとに、HTML, CSS, JSを1セットにしたものを作れる。しかもコンポーネントは再利用が可能。

コンポーネントの基本の書き方
<script>
Vue.component('コンポーネント名(タグ名)', {
  data() {
    // 処理
  },
  template: '描画したいHTMLの内容'
})
</script>
ex.)ボタンをクリックするとカウントが増えていく処理
<script src="https://cdn.jsdelivr.net/npm/vue"></script>

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

<script>
  Vue.component('button-counter', {
    data() {
      return {
        count: 0
      }
    },
    template: '<button v-on:click="count ++">{{ count }}</button>'
  })
  
  new Vue({
    el: '#app'
  })
</script>
以下のように、登録したコンポーネントは再利用できる。
<div id="app">
    <button-counter></button-counter>
    <button-counter></button-counter>
    <button-counter></button-counter>
</div>

コンポーネントから子コンポーネントへのデータの渡し方

コンポーネントを登録するとコンポーネント名が与えられるが、そのコンポーネント名は Vue.component の第一引数で指定すること(以下ではtodo-item)。

そして、Vueインスタンス(親コンポーネント)のdataは、子コンポーネントに記述しているpropsで受け取ることができる。

ちなみにprops: ['val']のvalは何でもおk。valに親のデータ(=data)が入ってくることは忘れないようにしたい。

templateには利用したいHTMLを記述する。以下ではtemplate: '<li>{{ val.text }}</li>'と記述している。

(index.html)
<div id="app">
  <ol>
    <todo-item
      v-for="item in groceryList"
      v-bind:val="item"
    ></todo-item>
  </ol>
</div>
(main.js)
<script>
// 子コンポーネント
Vue.component('todo-item', {
  props: ['val'],
  template: '<li>{{ val.text }}</li>'
})

// 親コンポーネント
new Vue({
  el: '#app',
  data: {
    groceryList: [
      { id: 0, text: 'Vegetables' },
      { id: 1, text: 'Cheese' },
      { id: 2, text: 'Whatever else humans are supposed to eat' }
    ]
  }
})
</script>
>> 出力結果
1. Vegetables
2. Cheese
3. Whatever else humans are supposed to eat

上記のmain.jsのtemplate内でval.idとすれば、描画内容が変わる。

(main.js)
<script>
// 子コンポーネント
Vue.component('todo-item', {
  props: ['val'],
  template: '<li>{{ val.id }}</li>'
})
...
.....
</script>
>> 出力結果
1. 0
2. 1
3. 2

グローバル登録とローカル登録

  • グローバル登録...全ての(ルート)Vueインスタンス(new Vue({ ... }))で利用できる。 グローバル登録したコンポーネントは使用しない場合でもビルドされるので、ダウンロードするJSのファイルサイズが大きくなり負荷がかかる。
  • ローカル登録..登録したVueインスタンスでのみ利用できる。
グローバル登録
<div id="app1">
  <h1>app1</h1>
  <hello-component></hello-component>
</div>

<div id='app2'>
  <h2>app2</h2>
  <hello-component></hello-component>
</div>
<script>
  // hello-component を登録
  Vue.component('hello-component', {
    template: '<div>Hello Vue.js</div>'
  })

  // 複数のVueインスタンス
  new Vue({
    el: '#app1'
  })
 new Vue({
  el: '#app2'
  })
</script>
>> 出力結果
app1
Hello Vue.js
app2
Hello Vue.js
ローカル登録

el: '#app1'で紐付けたDOM要素(<div id="app1">)にlocal-component1を登録。

<div id="app1">
  <h1>app1</h1>
  <local-component1></local-component1>
</div>

<div id="app2">
  <h2>app2</h2>
  <local-component1></local-component1>
</div>
<script>
// elで紐付けたapp1にだけ、local-component1を登録
new Vue({ 
    el: '#app1',
    components: {
      'local-component1': {
        template: '<div>local 1</div>'
      }
    } 
  })
</script>
>> 出力結果
app1
local1
app2

コンポーネントから親コンポーネントへのデータの渡し方

子でemitを使って、親にデータを渡す。

$emit('イベント名', 渡すデータA, 渡すデータB, ...);

以下では、子コンポーネントのボタンのクリック数を、親コンポーネントで参照している。

<script>
// 子コンポーネント
Vue.component('button-counter', {
    template: '<button v-on:click="childEvent">{{ counter }}</button>',
    data:  ()=> {
        return {
            counter: 0
        }
    },
    methods: {
        childEvent: function () {
            this.counter += 1
            this.$emit('increment')
        }
    },
})

// 親コンポーネント
new Vue({
    el: '#counter-event',
    data: {
        total: 0
    },
    methods: {
        parentEvent: function () {
            this.total += 1
        }
    }
})
</script>
<div id="counter-event">
    <p>クリック数:{{ total }}</p>
    <button-counter v-on:increment="parentEvent"></button-counter>
</div>
処理の流れ
  1. コンポーネントでボタンが押され、childEventが発火→子コンポーネントの数値が加算される。
  2. this.$emit('increment')でincrementイベントが発火→親コンポーネント(counter-eventというプロパティで紐付けたVueインスタンス)のparentEventメソッドが実行される。
  3. parentEventメソッドで親コンポーネントの数値が加算される。

出力結果
https://i.gyazo.com/a65dc8188bdfe157b4680f500131a376.mp4

ミックスイン