コンポーネントを用いることで UI を独立した再利用可能な部品に分割し、それぞれの部品を切り離して考えることができるようになります。アプリケーションはネストされたコンポーネントのツリーによって構成されることになります。
コンポーネントにはグローバルコンポーネントとローカルコンポーネントがあります。
グローバルコンポーネント
グローバルコンポーネントはアプリケーション内のどのコンポーネントのテンプレートでも使えます。
以下はグローバルコンポーネントです。id=”ex1″ のなかであれば <hato1></hato1>というひとつのタグで自分のサイトへのリンクを挿入することができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<div id="ex1"> <hato1></hato1> <hato2></hato2> </div> <script> let comp1 = { template: '<p><a href="https://lets-csharp.com/">鳩でもわかるC#</a></p>', } let comp2 = { template: '<p>vue.jsがわからず苦戦中</p>', } let app = Vue.createApp({}); app.component('hato1', comp1); // hato1とhato2をグローバルコンポーネントとして登録する app.component('hato2', comp2); app.mount('#ex1'); </script> |
グローバルコンポーネントなのでアプリケーション内のどのコンポーネントのテンプレートでも使えます。そのためこんなこともできます。hato3というコンポーネントを新しくつくり、そのなかでhato1とhato2を使用しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<div id="ex2"> <hato3></hato3> </div> <script> let comp1 = { template: '<p><a href="https://lets-csharp.com/">鳩でもわかるC#</a></p>', } let comp2 = { template: 'Vue.jsがわからず苦戦中', } let comp3 = { template: '<div v-bind:style="style"><hato1></hato1><hato2></hato2></div>', data: () => { return { style:'font-weight:bold', }; }, } let app = Vue.createApp({}); app.component('hato1', comp1); app.component('hato2', comp2); app.component('hato3', comp3); app.mount('#ex2'); </script> |
ローカルコンポーネント
ローカルコンポーネントはそのままでは他のコンポーネント内で使用できません。hato3のなかでhato1とhato2を使うのであれば以下のようにhato3のなかでhato1とhato2を登録して使います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
<div id="ex3"> <hato3></hato3> </div> <script> let comp1 = { template: '<p><a href="https://lets-csharp.com/">鳩でもわかるC#</a></p>', } let comp2 = { template: 'Vue.jsがわからず苦戦中', } let comp3 = { template: '<div v-bind:style="style"><hato1></hato1><hato2></hato2></div>', data: () => { return { style:'font-weight:bold', }; }, components:{ 'hato1':comp1, // この2つが必要 'hato2':comp2, }, } let app = { components:{ 'hato3':comp3, // 'hato1':comp1, ここで登録しても意味をなさない // 'hato2':comp2, }, }; Vue.createApp(app).mount('#ex3'); </script> |
データの受け渡し
親子関係にあるコンポーネントの間でデータを渡すときはどうすればよいでしょうか?
親から子へ
component1のなかにcomponent2があり、component1のなかにあるボタンをクリックしたら更新されたデータを表示させます。
1 2 3 |
<div id ="ex4"> <component1></component1> </div> |
子コンポーネントから定義します。親コンポーネントのテンプレート内にcomponent2タグがあるので、こちらを先に定義します(順番が逆だとエラー)。親からdataを受け取ったら配列を半角スペース区切りの文字列に変換して表示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<script> let component2 = { props : ['data',], // これが必要 template: `<div> <strong>子コンポーネントによる表示</strong><br> {{show()}}<br> </div>`, methods:{ show(){ return this.data.join(' '); }, }, } </script> |
親コンポーネントを定義します。ボタンがクリックされたら配列 arr にデータを追加または削除し、子コンポーネントにdataとしてarrを渡します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<script> let component1 = { template: `<div><input type="text" v-model="input"></div> <div><button @click="add">末尾に追加する</button></div> <div><button @click="remove">先頭を削除する</button></div> <br> <component2 v-bind:data="arr"></component2>`, data: () => { return { input:'', arr:['犬', '猫', '鳩'], }; }, methods:{ add(){ if(this.input != ''){ this.arr.push(this.input); this.input =''; } }, remove(){ this.arr.shift(); }, }, components:{ 'component2':component2, }, } </script> |
これで親コンポーネントで表示されているボタンをクリックすると、更新されたデータが子コンポーネントで表示されます。
1 2 3 4 5 6 7 8 9 |
<script> let app = { components:{ 'component1':component1, }, }; Vue.createApp(app).mount('#ex4'); </script> |
子から親へ
次は子コンポーネントから親コンポーネントにデータを渡す処理を考えます。
component1のなかにcomponent2があるのは同じですが、component2のなかにあるボタンをクリックしたら更新されたデータがcomponent1で表示されるようにします。
1 2 3 |
<div id ="ex5"> <component1></component1> </div> |
これは子コンポーネントです。最初とデータが追加または削除されたときに、カスタムイベント data-changedを発生させます。mounted()でもカスタムイベント data-changedを発生させています。これは最初に表示するデータを渡しておかないとなにも表示されないからです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<script> let component2 = { emits:['data-changed'], // 必要 data: () => { return { input:'', arr:['犬', '猫', '鳩'], }; }, template: `<input type="text" v-model="input"></div> <div><button @click="add">末尾に追加する</button></div> <div><button @click="remove">先頭を削除する</button>`, methods:{ add(){ if(this.input != ''){ this.arr.push(this.input); this.input =''; this.$emit("data-changed", this.arr); // 変更されたデータを送る } }, remove(){ this.arr.shift(); this.$emit("data-changed", this.arr); }, }, mounted(){ this.$emit("data-changed", this.arr); // 最初に表示すべきデータを送る } } </script> |
dataChangedを監視してイベントが発生したら前回同様に配列を半角スペース区切りの文字列に変換して表示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<script> let component1 = { data: () => { return { text:'', }; }, template: `<component2 v-on:dataChanged="dataChanged"></component2> <strong>親コンポーネントによる表示</strong><br> {{text}}`, methods:{ dataChanged(arr){ this.text = arr.join(' '); }, }, components:{ 'component2':component2, }, } </script> |
あとは前回と同じです。
1 2 3 4 5 6 7 8 9 |
<script> let app = { components:{ 'component1':component1, }, }; Vue.createApp(app).mount('#ex5'); </script> |