LoginSignup
2
1

More than 3 years have passed since last update.

【Vue.js - 5】コンポーネントでUI部品を作る

Posted at

まとめ

  • コンポーネントはJS&HTMLをセットで管理できる
  • 親から子は属性で渡してporpsで受け取る
  • 子から親は子が$emitでカスタムイベントで発火して親が受け取る
  • スロットはテンプレートがカスタマイズできる
  • コンポーネント開発を楽にしたいなら単一ファイルコンポーネントがおすすめ!

1. コンポーネントの定義

コンポーネントの登録

html
<div id="app">
  <my-component></my-component>
<div>
Vue.js
Vue.component('my-component', {
  template: '<p>MyComponent</p>',
  //データはオブジェクトを返す関数にしなければならない
  data: function() { 
    return { message: 'Hello!World' }
  },
  methods: {
  }
})

ローカルへの登録(new Vueする前にコンポーネントを定義する)

Vue.js
var myComponent = {
  template: '<p>MyComponent</p>'
}
new Vue({
  el: '#app',
  components: {
    'my-component': myComponent
  }
})

2. コンポーネント間の通信

コンポーネント間の通信(親から子)(props down)
プロパティとして文字列を渡す

html
<comp-child val="これは子A"></comp-child>
<comp-child val="これは子B"></comp-child>
Vue.js
Vue.component('comp-child', {
  //テンプレートで受け取ったvalを使用
  template: '<p>{{ val }}</p>',
  //受け取る属性名を指定
  props: ['val']
})

プロパティとしてデータを渡す

html
<comp-child :val="valueA"></comp-child>
<comp-child :val="valueB"></comp-child>
Vue.js
new Vue({
  data: {
    valueA: 'これは子A',
    valueB: 'これは子B'
  }
})

コンポーネントをリストレンダリング
子コンポーネント

html
<ul>
  <comp-child v-for="item in list"
    v-bind:key="item.id"
    v-bind:name="item.name"
    v-bind:hp="item.hp"></comp-child>
</ul>
Vue.js
Vue.component('comp-child', {
  template: '<li>{{ name }} HP.{{ hp }}</li>',
  props: ['name', 'hp']
})

親コンポーネント

Vue.js
new Vue({
  el: '#app',
  data: {
    list: [
      { id: 1, name: 'スライム', hp: 100 },
      { id: 2, name: 'ゴブリン', hp: 200 },
      { id: 3, name: 'ドラゴン', hp: 500 }
    ]
  }
})

エラーになるパターン

Vue.js
Vue.component('comp-child', {
  template: '<li>{{ name }} HP.{{ hp }}
  <button v-on:click="doAttack">攻撃する</button></li>',
  props: ['name', 'hp'],
  methods: {
    doAttack: function () {
      //propsで受け取ったデータは勝手に書き換えてはいけない!
      this.hp -= 10  //-> [Vue warn] error!
    }
  }
})

propsの受け取りデータ型を指定する

Vue.js
//タイプチェックを省略した場合
Vue.component('example', {
  props: ['value']  //どんな型も受け入れる
})
//データ型のみチェックする場合
Vue.component('example', {
  props: {
    value: データ型
  }
})

インスタンスのチェック

html
<example v-bind:value="value"></example>
Vue.js
function Cat(name) {
  this.name = name
}
Vue.component('example', {
  props: {
    value: Cat  //猫データのみ許可!
  }
})
new Vue({
  data: {
    value: new Cat('たま')  //valueは猫データ
  }
})

その他のオプションも使用する場合

Vue.js
Vue.component('example', {
  props: {
    value: {
      type: [String, Number], //許可するデータ型
      default: 100, //デフォルト値
      required: true, //必須にする
      validator: function (value) { //カスタムバリデータ関数、チェックして真偽値を返す
        return value > 10
      }
    }
  }
})

コンポーネント間の通信(子から親)(event up)
子のイベントを親にキャッチさせる

子コンポーネント

Vue.js
Vue.component('comp-child', {
  //クリックでhandleClickを実行
  template: '<button v-on:click="handleClick">イベント発火</button>',
  methods: {
    //ボタンのクリックイベントのハンドラでchilds-eventを発火する
    handleClick: function () {
      this.$emit('childs-event')
    }
  }
})

親コンポーネント

html
<comp-child v-on:childs-event="parentsMethod"></comp-child>
Vue.js
new Vue({
  el: '#app',
  methods: {
    //childs-eventが発生した!
    parentsMethod: function () {
      alert('イベントをキャッチ! ')
    }
  }
})

親が持つデータを操作

子コンポーネント

Vue.js
Vue.component('comp-child', {
  template: '<li>{{ name }} HP.{{ hp }}
  <button v-on:click="doAttack">攻撃する</button></li>',
  props: {
    id: Number,
    name: String,
    hp: Number
  },
  methods: {
    //ボタンのクリックイベントのハンドラから$emitでattackを発火する
    doAttack: function () {
      //引数として自分のIDを渡す
      this.$emit('attack', this.id)
    }
  }
})

親コンポーネント

html
<ul>
  <comp-child v-for="item in list"
    v-bind:key="item.id"
    v-bind="item"
    v-on:attack="handleAttack"></comp-child>
</ul>
Vue.js
new Vue({
  el: '#app',
  data: {
    list: [
      { id: 1, name: 'スライム', hp: 100 },
      { id: 2, name: 'ゴブリン', hp: 200 },
      { id: 3, name: 'ドラゴン', hp: 500 }
    ]
  },
  methods: {
    //attackが発生した!
    handleAttack: function (id) {
      //引数のIDから要素を検索
      var item = this.list.find(function (el) {
        return el.id === id
      })
      //HPが0より多ければ10減らす
      if (item !== undefined && item.hp > 0) item.hp -= 10
    }
  }
})

カスタムタグのイベントハンドリング

html
<!-- コンポーネント側からclickを$emitで呼び出さない限り発火しない -->
<my-icon v-on:click="handleClick"></my-icon>
<!-- .native修飾子を使い、元々のイベントを直接発火する -->
<my-icon v-on:click.native="handleClick"></my-icon>

コンポーネント間の通信(非親子)

Vue.js

//イベントバス用インスタンスを作成
var bus = new Vue({
  data: {
    count: 0
  }
})
Vue.component('component-b', {
  template: '<p>bus: {{ bus.count }}</p>',
  computed: {
    //busのデータを算出プロパティに使用
    bus: function () {
      return bus.$data
    }
  },
  //createdフックでbus-eventが起きた時の処理をかく
  created: function () {
    bus.$on('bus-event', function () {
      this.count++
    })
  }
})

コンポーネント間の通信(その他)
子コンポーネントを参照する$refs

親コンポーネント

html
<comp-child ref="child">
Vue.js
new Vue({
  el: '#app',
  methods: {
    handleClick: function () {
      //子コンポーネントのイベントを発火
      this.$refs.child.$emit('open')
    }
  }
})

子コンポーネント

Vue.js
Vue.component('comp-child', {
  template: '<div>...</div>',
  created: function () {
    //自分自身のイベント($onを使ってハンドルできる)
    this.$on('open', function () {
      console.log('なにか処理')
    })
  }
})

コンポーネントの属性のスコープ
コンポーネントの属性の値に当たる部分は、親のスコープになる

html
<comp-child v-on:childs-events="親のメソッド">
<comp-child v-on:childs-events="parentsMethod(親のデータ)">

子コンポーネントが引数を持って\$emitを実行している場合、
子コンポーネントの引数は\$event変数で使用できる

html
<comp-child v-on:childs-events="parentsMethod($event, parentsData)">
Vue.js
new Vue({
  el: '#app',
  data: {
    parentsData: '親のデータ'
  },
  methods: {
    parentsMethod: function(childsArg, parentsArg){
      //処理
    }
  }
})

\$event変数は\$emitの第1引数しか持たない
→複数の引数を渡したい場合は、1つのオブジェクトにまとめる

Vue.js
this.$emit('childs-event', { id: 1 name: '新しい名前'})
2
1
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
2
1