Vue入門12 実際にプログラムをコンポーネント化してみる

Vue入門の第12回(第1回はこちら)です。今回は、前々回までに作成した「靴下を買い物かごに入れるプログラム」を実際にコンポーネント化してみます。

現時点のプログラムを確認する

コンポーネント化の作業に入る前に、前々回までに作成した現時点のindex.htmlとmain.jsを確認しておきましょう。

現時点の index.html は、以下の通りです。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Vueサンプル</title>
    <script src="https://unpkg.com/vue"></script>
</head>
<body>
<div id="app">
	<div class="item">

		<div class="item-image">
		    <img v-bind:src="itemImage" />
		</div>

		<div class="item-info">
		    <h2>{{ itemFullName }}</h2>
		    <ul><li v-for="itemInfo in itemInfos">{{ itemInfo }}</li></ul>
		    <p v-if="zaiko > 2">在庫あり</p>
		    <p v-else-if="zaiko <= 2 && zaiko > 0">残りわずか!</p>
		    <p v-else>在庫なし</p>

		    <div 
		    v-for="itemColor in itemColors" 
		    v-on:click="changeColor(itemColor.cImage, itemColor.cName)" 
		    v-bind:style="{ backgroundColor: itemColor.cCode }" 
		    style="width:30px; height:30px; margin-bottom:20px;"
		    >
		    </div>

		    <button 
		    v-on:click="addCart" 
		    v-bind:disabled="soldout"
		    >買い物かごの中身を増やす</button>
		    <div class="cart">
		        <p>買い物かごの中身 {{ cart }}</p>
		    </div>
		</div>

	</div>
</div>
<script src="main.js"></script>
</body>
</html>

現時点の main.js は、以下の通りです。

var app = new Vue({
    el: '#app',
    data: {
        itemName: 'キッズ靴下',
        itemColorName: 'ピンク',
        itemImage: './images/socks-pink.png',
        itemInfos: ["子供向け靴下", "コットン100%", "人気商品!"],
        itemColors: [
            {cName:"ピンク", cCode:"#ee6699", cImage:"./images/socks-pink.png"},
            {cName:"ブルー", cCode:"#33aaee", cImage:"./images/socks-blue.png"}
        ],
        zaiko: 5,
        cart: 0,
        soldout: false
    },
    methods: {
        addCart: function () {
            this.cart += 1
            this.zaiko -= 1
            if (this.zaiko <= 0) {this.soldout = true}
        },
        changeColor (cImage, cName) {
            this.itemImage = cImage,
            this.itemColorName = cName
        }
    },
    computed: {
        itemFullName() {
            return this.itemName + '(' + this.itemColorName + ')'
        }
    }
})

以下は、上記プログラムの動作確認用のサンプルページです。 別画面で開きます。

https://programming-world.net/sample/vue_sample/10/index.html

Vueコンポーネントの書式を確認する

次に、前回学んだVueコンポーネントの書式を確認しておきましょう。

Vue.component('コンポーネント名', {
    template: `
        <div>
            <h1>・・・</h1>
            <p>・・・</p>
        </div>
    `,
    data() {
        return {
            //dataプロパティ
        }
    },
    methods: {
        //methodsプロパティ
    },
    computed: {
        //computedプロパティ
    }
})

Vueコンポーネントを作成する際には、Vue.component(‘コンポーネント名’, { ~ }) でひとつのコンポーネントを定義します。

そして、{ ~ } のなかにオプションとしてtemplate、data()、methods、computedなどのプロパティを記述します。

main.jsを書き換える

では、前々回までに作成した「靴下を買い物かごに入れるプログラム」を実際にコンポーネント化していきましょう。

現時点のmain.jsのバックアップを残したうえで、main.jsを以下のように書き換えます。

Vue.component('item', {
    /*
    ここにtemplate、data()、methods、computed
    などのプロパティを記述する
    */
})

var app = new Vue({
    el: '#app'
    /*
    ここに記述されていたdata、methods、computedプロパティは、
    上のitemコンポーネントに移動する
    */
})

Vue.component(‘item’, { ~ }) の部分がひとつのコンポーネントです。まだ「item」というコンポーネント名を指定しただけで、具体的なプロパティの記述はしていません。

上記コードは未完成ですが、var appから「靴下を買い物かごに入れるプログラム」の機能を、itemコンポーネントとして切り出しています。

main.jsを完成させる

上記コードのitemコンポーネント内に、これまでに作成したプログラムを流し込んでmain.jsを完成させます。

main.js を以下のように書き換えます。バックアップとして残しておいた旧main.jsからコピー&ペーストすると、効率よく書き換えられるでしょう。

Vue.component('item', {
    template: `
        <div class="item">
            <div class="item-image">
                <img v-bind:src="itemImage" />
            </div>
    
            <div class="item-info">
                <h2>{{ itemFullName }}</h2>
                <ul><li v-for="itemInfo in itemInfos">{{ itemInfo }}</li></ul>
                <p v-if="zaiko > 2">在庫あり</p>
                <p v-else-if="zaiko <= 2 && zaiko > 0">残りわずか!</p>
                <p v-else>在庫なし</p>
    
                <div 
                v-for="itemColor in itemColors" 
                v-on:click="changeColor(itemColor.cImage, itemColor.cName)" 
                v-bind:style="{ backgroundColor: itemColor.cCode }" 
                style="width:30px; height:30px; margin-bottom:20px;"
                >
                </div>
    
                <button 
                v-on:click="addCart" 
                v-bind:disabled="soldout"
                >買い物かごの中身を増やす</button>
                <div class="cart">
                    <p>買い物かごの中身 {{ cart }}</p>
                </div>
            </div>
        </div>
    `,
    data() {
        return {
            itemName: 'キッズ靴下',
            itemColorName: 'ピンク',
            itemImage: './images/socks-pink.png',
            itemInfos: ["子供向け靴下", "コットン100%", "人気商品!"],
            itemColors: [
                {cName:"ピンク", cCode:"#ee6699", cImage:"./images/socks-pink.png"},
                {cName:"ブルー", cCode:"#33aaee", cImage:"./images/socks-blue.png"}
            ],
            zaiko: 5,
            cart: 0,
            soldout: false
        }
    },
    methods: {
        addCart: function () {
            this.cart += 1
            this.zaiko -= 1
            if (this.zaiko <= 0) {this.soldout = true}
        },
        changeColor (cImage, cName) {
            this.itemImage = cImage,
            this.itemColorName = cName
        }
    },
    computed: {
        itemFullName() {
            return this.itemName + '(' + this.itemColorName + ')'
        }
    }
})

var app = new Vue({
    el: '#app'
})

上記コードは、前々回までに作成した「靴下を買い物かごに入れるプログラム」を、ほとんどコピー&ペーストで流し込んだだけですが、templateプロパティとdataプロパティの部分が若干異なります。

templateプロパティは、レンダリング用のテンプレートとなるHTMLタグを記述する部分です。これまではindex.html側で記述していたHTMLタグを、template: ` ~ ` のなかに記述しています。

dataプロパティは、戻り値を返す関数となっています。dataプロパティを関数にしておくことで、同じコンポーネントが複数利用される場合にもそれぞれ個別のdataインスタンスが生成されるようになります。

Vueコンポーネントにおけるtemplateプロパティやdataプロパティについては、前回も解説しています。合わせてご確認いただくと理解が深まるかもしれません。

index.htmlを書き換える

次に、index.htmlを以下のように書き換えます。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Vueサンプル</title>
    <script src="https://unpkg.com/vue"></script>
</head>
<body>
<div id="app">
    <item></item>
</div>
<script src="main.js"></script>
</body>
</html>

index.htmlの記述は大変スッキリしました。 <div id=”app”> ~ </div>をルート要素にして、そのなかに<item></item>という具合にitem要素がひとつだけ記述されています。

これまでindex.html内に記述していたHTMLタグを、main.js内のtemplate: ` ~ ` に移動させたため、index.html内に複雑なコードを記述する必要がなくなりました。

表示を確認してみる

ここまで出来たら、 index.htmlとmain.jsを上書き保存して表示を確認してみましょう。以下は、今回作成したサンプルページです。

https://programming-world.net/sample/vue_sample/12/index.html

前々回時点と同じように動作していれば成功です。

コンポーネントにすると再利用性が高まる

プログラムをコンポーネント化するメリットのひとつは、再利用性が高まることです。例えば、「靴下を買い物かごに入れるプログラム」をコンポーネント化したことで、複数の買い物かごをウェブページ内に簡単に配置できます。

以下の使用例では、itemコンポーネントを3つ並べています。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Vueサンプル</title>
    <script src="https://unpkg.com/vue"></script>
</head>
<body>
<div id="app">
    <item></item>
    <item></item>
    <item></item>
</div>
<script src="main.js"></script>
</body>
</html>

以下は、動作確認用のサンプルページです。別画面で開きます。

https://programming-world.net/sample/vue_sample/12/index3.html

同じ機能を持つ3つの買い物かごが作成できていれば成功です。

<item></item>というタグを3つ並べるだけで、3つの買い物かごが簡単に作成できました。dataプロパティの部分を戻り値を返す関数としているので、3つのitemコンポーネントはそれぞれ個別のデータを扱う独立した買い物かごとして動作します。コンポーネントが再利用できることを実感していただければ幸いです。

Vueでは、このようにウェブページをコンポーネント単位でモジュール化して、ブロックのパーツを組み合わせるようにしてアプリケーションを作成していきます。

次回へ続きます。