CodeZine(コードジン)

特集ページ一覧

Vue.jsでWebページをつくる際の肝!「コンポーネント」をTypeScriptで活用しよう

TypeScriptで学ぶJavaScriptフレームワーク「Vue.js」の利用法 第6回

  • LINEで送る
  • このエントリーをはてなブックマークに追加

 本連載では、JavaScriptフレームワーク「Vue.js」を、型定義が利用できるようJavaScriptを拡張した言語「TypeScript」で活用する方法を、順を追って説明していきます。前回はVue 3がデフォルトとなる時代のVue.js開発新常識を紹介しました。今回は、Vue.jsでWebページを作る際の構成要素となる「コンポーネント」について説明していきます。

目次

はじめに

 本連載では、JavaScriptを利用して動的なWebページを構築できるフレームワークVue.jsを、データの型指定ができるように拡張されたAltJS(コンパイルしてJavaScriptにする言語)であるTypeScriptで活用する方法を、順を追って説明しています。

 Vue.jsにおいて、Webページのある一部分を構成する実装単位を「コンポーネント」と呼びます。これまでの連載記事では、Webページ全体に対応するコンポーネント(App.vueファイル)を単体で利用してきましたが、より表示が複雑な実際の開発では、1つのWebページを複数のコンポーネントに分割して開発するのが一般的です。そこで本記事では、複数のコンポーネントを利用してWebページを作成する方法を説明します。

対象読者

  • これからVue.jsに入門したい方
  • 新しいトレンドを常に取り入れたい方
  • 比較的複雑なWebページをVue.jsで作ってみたい方

必要な環境

 本記事のサンプルコードは、以下の環境で動作を確認しています。

  • Windows 10 64bit版
  • Node.js v16.15.1 64bit版
  • Vue.js 3.2.36
  • TypeScript 4.7.2
  • Microsoft Edge 102.0.1245.39

 サンプルコードを実行するには、サンプルのフォルダーで「npm install」コマンドを実行してライブラリーをダウンロード後、「npm run dev」コマンドを実行して、Webブラウザーで「http://localhost:3000/」を表示します。

 サンプルコードの*.vueファイルには、表示を調整するため<style>部が記述されている場合がありますが、記事中では省略します。詳細はサンプルコードを参照してください。

親から子に情報を伝達する、コンポーネントのプロパティ(props)

 最初に、スマートフォンのリストが表示される図1のサンプルで、コンポーネントの利用法を説明していきます。Webページ全体に対応するコンポーネント(App.vueファイル)の配下に、リストの1行に対応する子コンポーネントを複数配置します。

図1 コンポーネントにデータを設定して表示するサンプル(p001-props)
図1 コンポーネントにデータを設定して表示するサンプル(p001-props)

親コンポーネント側の実装

 他のコンポーネントを含める親となるApp.vueファイルはリスト1の通りです。

[リスト1]図1の親コンポーネント(p001-props/src/App.vue)
<template>
  <div>
    <!-- Phone1コンポーネントの描画 ...(1)-->
    <Phone1 name="Galaxy S22" vendor="Samsung" :price="125030"></Phone1>
    <Phone1 name="AQUOS sense6" vendor="SHARP" :price="57024"></Phone1>
    <!-- Phone2コンポーネントの描画 ...(2)-->
    <div v-for="phone in phoneDataArray" v-bind:id="phone.name">
      <Phone2 :phoneData="phone"></Phone2>
    </div>
  </div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
// 子コンポーネントをインポート ...(3)
import Phone1 from './components/Phone1.vue'
import Phone2 from './components/Phone2.vue'
// PhoneDataクラスをインポート ...(4)
import PhoneData from './PhoneData'
export default defineComponent({
  // 使用するコンポーネントを指定 ...(5)
  components: {
    Phone1,
    Phone2
  },
  // Composition APIでAppコンポーネントを初期設定
  setup() {
    const phoneDataArray = [ // phoneDataArrayの初期化 ...(6)
      new PhoneData('Xperia 1 IV', 'Sony', 192930),
      new PhoneData('arrows We', 'FCNT', 22000)
    ]
    return { phoneDataArray }
  }
})
</script>

 (1)では<Phone1>、(2)では<Phone2>コンポーネントを利用して、スマートフォンの1行を描画します。このようにコンポーネントを使用するには、<script>部の(3)でコンポーネントの実装を別ファイルからインポートして、defineComponentメソッド内のcomponents(5)に設定します。

 (1)のPhone1ではname(名前)、vendor(メーカー)、price(価格)を属性値としてコンポーネントに渡しています。「:price」は「v-bind:price」の省略で、priceに文字列型ではなく数字型でデータを渡すための記述です。

 一方(2)では、phoneDataArray配列の各要素をv-forディレクティブで抽出して、Phone2コンポーネントのphoneData属性に与えています。phoneDataArray配列は<script>部の(6)で定義されており、(4)でインポートしたPhoneDataクラスオブジェクトの配列になっています。なお、PhoneDataクラスにはname、vendor、priceのプロパティとコンストラクター、カンマ区切りの価格を取得するdispPriceプロパティが実装されています。詳細はサンプルコードを参照してください。

子コンポーネント側の実装

 次に、App.vueから参照される子コンポーネントの実装を見ていきます。まずPhone1コンポーネントはリスト2の通りです。

[リスト2]Phone1コンポーネントの実装(p001-props/src/components/Phone1.vue)
<template>
  <div class="elem-body">
    <div class="elem-name">{{ name }}</div>
    <div>
      【メーカー】{{ vendor }}
      【価格】{{ Intl.NumberFormat('ja-JP').format(price) }}円 {{/*(1)*/}}
    </div>
  </div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
  props: { // プロパティ ...(2)
    vendor: String,   // 文字列型 ...(2a)
    name: String,     // 文字列型
    price: {  // より細かい指定を行う例 ...(2b)
      type: Number,   // 数字型
      default: 0,     // 初期値0
      required: true  // 必須
    }
  }
})
</script>

 コンポーネントに指定するプロパティは、<script>部のdefineComponentメソッド内のprops(2)に指定します。各プロパティには(2a)や(2b)の記述で、型や初期値、必須かどうかを指定できます。ここでは文字列型のvendorとname、数字型(初期値0、必須)のpriceプロパティを定義しています。これらのプロパティを<template>部で参照してコンポーネントを表示します。なお(1)は、priceの値をカンマ区切りにする実装です。

 コンポーネントのプロパティには文字列や数字だけではなく、特定の型のオブジェクトを渡すこともできます。Phone2コンポーネントの実装(リスト3)で説明します。

[リスト3]Phone2コンポーネントの<script>部(p001-props/src/components/Phone2.vue)
import { defineComponent } from "vue"
import PhoneData from "@/PhoneData" // PhoneDataクラスをインポート ...(1)
export default defineComponent({
  props: {
    phoneData: PhoneData  // PhoneData型プロパティ ...(2)
  }
})

 (1)でPhoneDataクラスをインポートします。「@/PhoneData」は、プロジェクトのルートに存在するPhoneData.tsをインポートする意味です。defineComponentメソッドのpropsに(2)の通り記述して、phontDataプロパティの型をPhoneDataクラスに指定できます。

子から親にイベントを伝達するemit

 以下で紹介するemitは、子コンポーネントで発生したイベントを親コンポーネントに伝える仕組みです。子コンポーネント内で「購入する」ボタンを押すと、子コンポーネントの内容に対応したダイアログ表示を行う図2のサンプルで説明します。

図2 子コンポーネントからイベントを発生させるサンプル(p002-emit)

図2 子コンポーネントからイベントを発生させるサンプル(p002-emit)

 親コンポーネント(App.vue)はリスト4の通りです。

[リスト4]図2の親コンポーネント(p002-emit/src/App.vue)
<template>
  <div>
    <div v-for="phone in phoneDataArray" v-bind:id="phone.name">
      <PhoneWithBuy :phoneData="phone"
        @onBuy="onBuyFromComponent"><!-- onBuyイベントを設定 ...(1)-->
      </PhoneWithBuy>
    </div>
  </div>
</template>
<script lang="ts">
(略)
export default defineComponent({
  components: {
    PhoneWithBuy
  },
  setup() {
    const phoneDataArray = [
      new PhoneData('Galaxy S22', 'Samsung', 125030),
(略)
    ]
    // PhoneWithBuyコンポーネントのonBuyイベントハンドラー ...(2)
    const onBuyFromComponent = ((phone: PhoneData) => {
      alert(`${phone.name}(${phone.dispPrice}円)を購入します。`)
    })
    return {
      phoneDataArray, onBuyFromComponent
    }
  }
})
</script>

 (1)の記述で、コンポーネントのonBuyイベント発生時に(2)のonBuyFromComponentメソッドが実行されるようになります。このメソッドでは、引数に渡されるPhoneDataクラスの変数phoneから、nameとdispPriceプロパティを取得してダイアログ表示します。

 onBuyイベントは子コンポーネント(PhoneWithBuy.vue)でリスト5の通り実装します。

[リスト5]図2の子コンポーネントの<script>部(p002-emit/src/components/PhoneWithBuy.vue)
export default defineComponent({
(略:propsの指定)
  emits: [
    'onBuy' // emitする可能性があるイベントを指定 ...(1)
  ],
  setup(props, context) { // setupの引数でpropsとcontextを受け取る ...(2)
    // 購入するボタンクリック時の処理
    const onClickBuyButton = (() => {
      context.emit('onBuy', props.phoneData) // onBuyイベントをemit ...(3)
    })
    return { onClickBuyButton }
  }
})

 まずdefineComponentで、コンポーネントが送信(emit)する可能性があるイベント名(ここではonBuy)を、(1)のemits配列に指定します。Composition APIのsetupメソッド(2)では、第1引数にプロパティに対応するprops変数、第2引数にemitメソッドを含むcontext変数が受け取れます。ボタン押下時に実行されるonClickBuyButtonメソッドでは、(3)のcontext.emitメソッドの第1引数にイベント名onBuy、第2引数に引数(ここではprops.phoneDataプロパティ)を渡してイベントを発行します。


  • LINEで送る
  • このエントリーをはてなブックマークに追加

バックナンバー

連載:TypeScriptで学ぶJavaScriptフレームワーク「Vue.js」の利用法

著者プロフィール

  • WINGSプロジェクト  吉川 英一(ヨシカワ エイイチ)

    <WINGSプロジェクトについて> 有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂...

  • 山田 祥寛(ヤマダ ヨシヒロ)

    静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for ASP/ASP.NET。執筆コミュニティ「WINGSプロジェクト」代表。 主な著書に「入門シリーズ(サーバサイドAjax/XM...

あなたにオススメ

All contents copyright © 2005-2022 Shoeisha Co., Ltd. All rights reserved. ver.1.5