【輪読会資料】基礎から学ぶVue.js CHAPTER5 コンポーネントでUI部品を作る 読書メモ
今回は輪読担当ではなかったので、完全な自分のメモですが、アップしておきます。
var で定義できるローカルのコンポーネントもある、
// コンポーネントを定義 var myComponent = { template: '<p>MyComponets</p>' } new Vue({ el: '#app', componets: { // 処理 'myComponet' : myComponet } })
コンポーネント間の通信
P155
親から子
// コンポーネントの定義 comp-child Vue.component('comp-child', { // テンプレートで受け取ったvalを使用 template: '<p>{{ val }}</p>', // 受け取る属性名を指定 props: ['val'] }) // 親コンポーネント new Vue({ el: '#app', data: { valueA: 'これは子A', valueB: 'これは子B' } })
<div id="app"> <comp-child val="これは子A"></comp-child> <comp-child val="これは子B"></comp-child> </div>
出力結果 子コンポーネントの
で囲む状態が出力されている
<p>これは子A</p> <p>これは子B</p>
props
で受け取ってないものは子供側では上書きが基本、class等の属性は親と子でマージされ両方反映
コンポーネントのルートタグ
Vue.component('comp-child', { template: '<p id="child" class="child">ChildComponent</p>', })
コンポーネントのカスタムタグ
<comp-child id="parent" class="parent"></comp-child>
出力される実態
<comp-child id="parent" class="child parent">ChildComponent</comp-child>
// 子 Vue.component('comp-child', { template: '<li>{{ name }} HP.{{ hp }}</li>', props: ['name', 'hp'] }) // 親 new Vue({ el: '#app', data: { list: [ { id: 1, name: 'スライム', hp: 100 }, { id: 2, name: 'ゴブリン', hp: 200 }, { id: 3, name: 'ドラゴン', hp: 500 } ] } })
list は親を使っている name, hp は 子のprops定義で使える様になっている
<!-- これも親となる comp-child で子を呼び出し--> <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>
子コンポーネントで値を書き換えてもconsoleに[Vue warn]が出力される。
当然親の値は書き換えられない
Vue.component('comp-child', { template: '<li>{{ name }} HP.{{ hp }}\ <button v-on:click="doAttack">攻撃する</button></li>', props: ['name', 'hp'], methods: { doAttack: function () { // 勝手に攻撃! this.hp -= 10 // -> [Vue warn] error! } } })
もし、値の書き換えを行いたいなら算出プロパティを使って新しいデータを作成する。
もし親のデータ自体を変更したい場合は、$emit
を使って親にアクションを起こさせる。等する
props
で値を受け取る際は、データ型を指定しておくのが推奨されている。
Vue.component('comp-child', { props: { val: String // 文字列型のみ許可 } ])
型の指定方法一覧
データ型 | 説明 | 例 |
---|---|---|
String | 文字列 | '1' |
Number | 数値 | 1 |
Boolean | 真偽値 | true, false |
Function | 関数 | function() {} |
Object | オブジェクト | { name: 'foo' } |
Array | 配列 | [1, 2, 3], [{ id: 1 }, { id: 2 }] |
カスタム | インスタンス | new Cat() |
null | すべての型 | 1, '1', [1] |
型チェック無しの場合の書き方
Vue.component('example', { props: ['value'] // どんな型も受け入れる })
型チェックする場合の書き方
Vue.component('example', { props: { value: // ここにデータ型を指定 } })
型チェック以外にオプションで様々なバリデーションに対応できる
参照元
ほとんどControllerみたいになってきた。
Vue.component('my-component', { props: { // 基本的な型の検査 (`null` と `undefined` は全てのバリデーションにパスします) propA: Number, // 複数の型の許容 propB: [String, Number], // 文字列型を必須で要求する propC: { type: String, required: true }, // デフォルト値つきの数値型 propD: { type: Number, default: 100 }, // デフォルト値つきのオブジェクト型 propE: { type: Object, // オブジェクトもしくは配列のデフォルト値は // 必ずそれを生み出すための関数を返す必要があります。 default: function () { return { message: 'hello' } } }, // カスタマイズしたバリデーション関数 propF: { validator: function (value) { // プロパティの値は、必ずいずれかの文字列でなければならない return ['success', 'warning', 'danger'].indexOf(value) !== -1 } } } })
子から親
P161
字のデータを親に渡すには$emit
を使う
親のコード
on で受け取る
<child-comp v-on:childs-event="parentMethod">
子のコード
$emitで渡す
this.$emit('childs-event')
parentsMethod
がchilds-event
を通じて親に渡る
<comp-child v-on:childs-event="parentsMethod"></comp-child>
Vue.component('comp-child', { template: '<button v-on:click="handleClick">イベント発火</button>', methods: { // ボタンのクリックイベントのハンドラでchilds-eventを発火する handleClick: function () { // `parentsMethod`が`childs-event`を通じて親に渡る this.$emit('childs-event') } } }) new Vue({ el: '#app', methods: { // childs-eventが発生した! parentsMethod: function () { alert('イベントをキャッチ! ') } } })
親が持つデータを操作する
前述のモンスターのHPを子のメソッドで処理していた例を動くものにしたのが以下の例
良く読み込んで動きはなんとか理解はしたけど、今は書ける気がしない...。
<ul> <comp-child v-for="item in list" v-bind:key="item.id" v-bind:name="item" v-on:attack="handleAttack"></comp-child> </ul>
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を渡す v-on:attackの中身`handleAttack`を親に渡す this.$emit('attack', this.id) } } }) 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 } } })
カスタムタグのイベントハンドリンク
この書き方だと、コンポーネント側からclick
を$emit
で呼び出さないと発火しないらしい。
<my-icon v-on:click="handleClick"></my-icon>
元々のイベントは直接発火したい場合は.native
修飾子を付ける。
<my-icon v-on:click.native="handleClick"></my-icon>
非親子コンポーネント
親子関係以外だと、this
,props
の通信はできない。
Vueインスタンスを「イベントバス」として使用するが、多用しない方が良い。コードがカオス化するので、詳細はP166参照
子コンポーネントを参照する「$refs」
親コンポ―ネント
<comp-child ref="child">
親のメソッド内
new Vue({ el: '#app', methods: { handleClick: function () { // 子コンポーネントのイベントを発火 this.$refs.child.$emit('open') } } })
子コンポーネント内
Vue.component('comp-child', { template: '<div>...</div>', created: function () { // 自分自身のイベント this.$on('open', function () { console.log('なにか処理') }) } })
親側からはv-on
を使うようにする
コンポーネントの属性のスコープ
コンポーネントの属性の値部分は親のスコープになる
<comp-child v-on:child-event="親のメソッド">
<comp-child v-on:child-event="parentMethod(親のデータ)">
子コンポーネントが引数をもって$emit
を実行している場合、子コンポーネントの引数は$event
変数で使用できる
<comp-child v-on:child-event="parentMethod($event, parantsData)">
new Vue({ el: '#app', data: { parentsData: '親のデータ' }, methods: { methods: { parentsMethod: function(childArg, parentsArg) { // } } } })
$event
変数は$emit
の第一引数しか持たないので、複数の引数を渡す際は1つのオブジェクトにまとめる。
this.$emit('childs-event', {id: 1, name: '新しい名前'})