Vue単一ファイルコンポーネントを使用して再利用可能なコードブロックを作成する方法

提供:Dev Guides
移動先:案内検索

著者は、 Write for DOnations プログラムの一環として、 Open Sourcing MentalIllnessを選択して寄付を受け取りました。

序章

Vue.js を使用してWebアプリケーションを作成する場合は、小さなモジュール式のコードブロックでアプリケーションを構築することをお勧めします。 これにより、アプリケーションの一部に焦点が当てられるだけでなく、複雑さが増すにつれてアプリケーションの更新が容易になります。 Vue CLI から生成されたアプリにはビルド手順が必要なため、シングルファイルコンポーネント(SFC)にアクセスして、アプリにモジュール性を導入できます。 SFCには.vue拡張子があり、HTML <template><script>、および<style>タグが含まれており、他のコンポーネントに実装できます。

SFCを使用すると、開発者は、コンポーネントごとに独自の HTML タグを作成し、それらをアプリケーションで使用することができます。 <p> HTMLタグがブラウザで段落をレンダリングし、レンダリングされない機能も保持するのと同じように、コンポーネントタグはVueテンプレートに配置されている場所にSFCをレンダリングします。

このチュートリアルでは、SFCを作成し、propsを使用してデータを渡し、slotsを使用してタグ間にコンテンツを挿入します。 このチュートリアルを終了するまでに、SFCとは何か、およびコードの再利用性にアプローチする方法についての一般的な理解が得られます。

前提条件

ステップ1—プロジェクトの設定

このチュートリアルでは、一連のカードにいくつかの空港とそのコードを表示する空港カードコンポーネントを作成します。 前提条件のセクションに従うと、sfc-projectという名前の新しいVueプロジェクトが作成されます。 このセクションでは、この生成されたアプリケーションにデータをインポートします。 このデータは、ブラウザに情報を表示するために使用するいくつかのプロパティで構成されるオブジェクトの配列になります。

プロジェクトが生成されたら、ターミナルとcdを開くか、ディレクトリをルートsrcフォルダに変更します。

cd sfc-project/src

そこから、mkdirコマンドを使用してdataという名前の新しいディレクトリを作成し、touchコマンドを使用してus-airports.jsという名前の新しいファイルを作成します。

mkdir data
touch data/us-airports.js

選択したテキストエディタで、この新しいJavaScriptファイルを開き、次のローカルデータを追加します。

sfc-project / data / us-airports.js

export default [
  {
    name: 'Cincinnati/Northern Kentucky International Airport',
    abbreviation: 'CVG',
    city: 'Hebron',
    state: 'KY'
  },
  {
    name: 'Seattle-Tacoma International Airport',
    abbreviation: 'SEA',
    city: 'Seattle',
    state: 'WA'
  },
  {
    name: 'Minneapolis-Saint Paul International Airport',
    abbreviation: 'MSP',
    city: 'Bloomington',
    state: 'MN'
  }
]

このデータは、米国内のいくつかの空港で構成されるオブジェクト配列です。 これは、チュートリアルの後半で単一ファイルコンポーネントにレンダリングされます。

ファイルを保存して終了します。

次に、別の空港データのセットを作成します。 このデータはヨーロッパの空港で構成されます。 touchコマンドを使用して、eu-airports.jsという名前の新しいJavaScriptファイルを作成します。

touch data/eu-airports.js

次に、ファイルを開き、次のデータを追加します。

sfc-project / data / eu-airports.js

export default [
  {
    name: 'Paris-Charles de Gaulle Airport',
    abbreviation: 'CDG',
    city: 'Paris',
    state: 'Ile de France'
  },
  {
    name: 'Flughafen München',
    abbreviation: 'MUC',
    city: 'Munich',
    state: 'Bavaria'
  },
  {
    name: 'Fiumicino "Leonardo da Vinci" International Airport',
    abbreviation: 'FCO',
    city: 'Rome',
    state: 'Lazio'
  }
]

このデータセットは、それぞれフランス、ドイツ、イタリアのヨーロッパの空港に関するものです。

ファイルを保存して終了します。

次に、ルートディレクトリで、ターミナルで次のコマンドを実行して、ローカル開発サーバーで実行されているVueCLIアプリケーションを起動します。

npm run serve

これにより、localhost:8080のブラウザでアプリケーションが開きます。 ポート番号は、マシンによって異なる場合があります。

ブラウザでアドレスにアクセスします。 次の起動画面が表示されます。

次に、新しいターミナルを起動し、srcフォルダにあるApp.vueファイルを開きます。 このファイルで、<template>およびcomponentsセクションのimgおよびHelloWorldタグと、<script>App.vueは次のようになります。

sfc-project / src / App.vue

<template>

</template>

<script>
export default {
  name: 'App',
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

その後、先ほど作成したus-airports.jsファイルをインポートします。 このデータをリアクティブにして<template>で使用できるようにするには、vueからref関数をインポートする必要があります。 データをHTMLテンプレートで使用できるように、airport参照を返す必要があります。

次の強調表示された行を追加します。

sfc-project / src / App.vue

<template>
  <div class="wrapper">
    <div v-for="airport in airports" :key="airport.abbreviation" class="card">
      <p>{{ airport.abbreviation }}</p>
      <p>{{ airport.name }}</p>
      <p>{{ airport.city }}, {{ airport.state }}</p>
    </div>
  </div>
</template>

<script>
import { ref } from 'vue'
import data from '@/data/us-airports.js'

export default {
  name: 'App',
  setup() {
    const airports = ref(data)

    return { airports }
  }
}
</script>
...

このスニペットでは、データをインポートし、テンプレートの<div>要素とv-forディレクティブを使用してレンダリングしました。

この時点で、データがインポートされ、App.vueコンポーネントで使用できるようになります。 ただし、最初に、ユーザーがデータを読みやすくするためにスタイルを追加します。 この同じファイルで、<style>タグに次のCSSを追加します。

sfc-project / src / App.vue

...
<style>
#app { ... }

.wrapper {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  grid-column-gap: 1rem;
  max-width: 960px;
  margin: 0 auto;
}

.card {
  border: 3px solid;
  border-radius: .5rem;
  padding: 1rem;
  margin-bottom: 1rem;
}

.card p:first-child {
  font-weight: bold;
  font-size: 2.5rem;
  margin: 1rem 0;
}

.card p:last-child {
  font-style: italic;
  font-size: .8rem;
}
</style>

この場合、 CSSグリッドを使用して、これらの空港コードのカードを3つのグリッドに構成しています。 このグリッドが.wrapperクラスでどのように設定されているかに注目してください。 .cardクラスは、各空港コード、名前、および場所を含むカードまたはセクションです。 CSSの詳細については、CSSを使用してHTMLのスタイルを設定する方法をご覧ください。

ブラウザを開き、localhost:8080に移動します。 空港コードと情報が記載されたカードがいくつかあります。

最初のアプリを設定したので、次のステップでデータを単一ファイルコンポーネントにリファクタリングできます。

ステップ2—単一ファイルコンポーネントの作成

VueCLIはWebpackを使用して、ブラウザーが読み取れるものにアプリを構築するため、アプリはプレーンJavaScriptの代わりにSFCまたは.vueファイルを使用できます。 これらのファイルは、スケーラブルで再利用可能なコードの小さなブロックを作成するための方法です。 1つのコンポーネントを変更すると、どこでも更新されます。

これらの.vueコンポーネントは通常、<template><script>、および<style>要素の3つで構成されます。 SFCコンポーネントは、スコープ付きまたはスコープなしスタイルのいずれかを持つことができます。 コンポーネントにスコープスタイルがある場合、<style>タグ間のCSSは、同じファイル内の<template>内のHTMLにのみ影響します。 コンポーネントにスコープ外のスタイルがある場合、CSSは親コンポーネントとその子に影響します。

プロジェクトが正常にセットアップされたら、これらの空港カードをAirportCards.vueというコンポーネントに分割します。 現在のところ、App.vueのHTMLはあまり再利用できません。 これを独自のコンポーネントに分割して、機能とビジュアルを維持しながら、他の場所からこのアプリにインポートできるようにします。

ターミナルで、componentsディレクトリに次の.vueファイルを作成します。

touch src/components/AirportCards.vue

テキストエディタでAiportCards.vueコンポーネントを開きます。 コンポーネントを使用してコードのブロックを再利用する方法を説明するために、ほとんどのコードをApp.vueファイルからAirportCards.vueコンポーネントに移動します。

sfc-project / src / components / AirportCards.vue

<template>
  <div class="wrapper">
    <div v-for="airport in airports" :key="airport.abbreviation" class="card">
      <p>{{ airport.abbreviation }}</p>
      <p>{{ airport.name }}</p>
      <p>{{ airport.city }}, {{ airport.state }}</p>
    </div>
  </div>
</template>

<script>
import { ref } from 'vue'
import data from '@/data/us-airports.js'

export default {
  name: 'Airports',
  setup() {
    const airports = ref(data)

    return { airports }
  }
}
</script>

<style scoped>
.wrapper {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  grid-column-gap: 1rem;
  max-width: 960px;
  margin: 0 auto;
}

.card {
  border: 3px solid;
  border-radius: .5rem;
  padding: 1rem;
  margin-bottom: 1rem;
}

.card p:first-child {
  font-weight: bold;
  font-size: 2.5rem;
  margin: 1rem 0;
}

.card p:last-child {
  font-style: italic;
  font-size: .8rem;
}
</style>

ファイルを保存して閉じます。

次に、App.vueファイルを開きます。 これで、App.vueコンポーネントをクリーンアップして、AirportCards.vueをインポートできます。

sfc-project / src / App.vue

<template>
  <AirportCards />
</template>

<script>
import AirportCards from '@/components/Airports.vue'

export default {
  name: 'App',
  components: {
    AirportCards
  }
}
</script>

<style scoped>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

AirportCardsがスタンドアロンコンポーネントになったので、<p>タグと同じように、<template>HTMLに配置しました。

ブラウザでlocalhost:8080を開いても、何も変わりません。 <AirportCards />要素で新しいSFCをレンダリングしているため、同じ3つの空港カードが引き続き表示されます。

次に、この同じコンポーネントをテンプレートに再度追加して、コンポーネントの再利用性を示します。

/src/App.vue

<template>
  <AirportCards />
  <airport-cards />
</template>
...

AirportCards.vueのこの新しいインスタンスは、PascalCaseではなくkebab-caseを使用していることに気付くかもしれません。 コンポーネントを参照するとき、Vueはどのコンポーネントを使用するかを気にしません。 大文字の単語と文字はすべてハイフンで区切られ、小文字になります。 同じことが小道具にも当てはまります。これについては次のセクションで説明します。

注:使用するケースは個人の好み次第ですが、一貫性が重要です。 Vue.jsは、HTML標準に準拠しているため、kebab-caseの使用をお勧めします。


ブラウザを開き、localhost:8080にアクセスします。 カードが複製されていることがわかります。

これにより、アプリにモジュール性が追加されますが、データは静的です。 カードの列は、同じ3つの空港を表示する場合に役立ちますが、データソースを変更するには、ハードコードされたデータを変更する必要があります。 次のステップでは、小道具を登録し、親から子コンポーネントにデータを渡すことによって、このコンポーネントをさらに拡張します。

ステップ3—小道具を活用してデータを渡す

前の手順では、us-airports.jsファイルのデータから多数のカードをレンダリングするAirportCards.vueコンポーネントを作成しました。 それに加えて、同じコンポーネント参照を2倍にして、<template>にそのコンポーネントの別のインスタンスを追加することでコードを簡単に複製する方法を示しました。

ただし、データを静的なままにしておくと、将来的にデータを変更することが困難になります。 SFCを使用する場合、コンポーネントを関数と考えると役立ちます。 これらの関数は、引数(props)を受け取り、何か(HTML)を返すことができるコンポーネントです。 この場合、データをairportsパラメータに渡して、ダイナミックHTMLを返します。

テキストエディタでAirportCards.vueコンポーネントを開きます。 現在、us-airports.jsファイルからdataをインポートしています。 このインポートステートメントと、<script>タグのsetup関数を削除します。

sfc-project / src / components / AirportCards.vue

...
<script>

export default {
  name: 'Airports',
}
</script>
...

ファイルを保存します。 この時点では、ブラウザには何も表示されません。

次に、小道具を定義して前に進みます。 この小道具には何でも名前を付けることができます。 入ってくるデータを説明し、それを名前に関連付けるだけです。

小道具を作成するには、コンポーネントにpropsプロパティを追加します。 この値は、一連のキーと値のペアです。 キーは小道具の名前であり、値はデータの説明です。 できるだけ多くの説明を提供することをお勧めします。

sfc-project / src / components / AirportCards.vue

...
<script>

export default {
  name: 'Airports',
  props: {
    airports: {
      type: Array,
      required: true
    }
  }
}
</script>
...

現在、AirportCard.vueでは、プロパティairportsは渡されるデータを参照しています。 ファイルを保存して終了します。

次に、テキストエディタでApp.vueコンポーネントを開きます。 以前と同様に、us-airports.jsファイルのimportデータとVueのimport関数を使用して、HTMLテンプレートに反応するようにする必要があります。

sfc-project / src / App.vue

<template>
  <AirportCards :airports="usAirports" />
  <airport-cards />
</template>

<script>
import { ref } from 'vue'
import AirportCards from '@/components/Airports.vue'
import usAirportData from '@/data/us-airports.js'

export default {
  name: 'App',
  components: {
    AirportCards
  },
  setup() {
    const usAirports = ref(usAirportData)

    return { usAirports }
  }
}
</script>

ブラウザを開いてlocalhost:8080にアクセスすると、以前と同じ米国の空港が見つかります。

テンプレートには別のAirportCards.vueインスタンスがあります。 そのコンポーネント内で小道具を定義したので、同じ構造の任意のデータを渡して、さまざまな空港からの多数のカードをレンダリングできます。 ここで、初期設定のeu-airports.jsファイルが入ります。

App.vueで、eu-airports.jsファイルをインポートし、ref関数でラップしてリアクティブにし、次のように返します。

/src/App.vue

<template>
  <AirportCards :airports="usAirports" />
  <airport-cards :airports="euAirports" />
</template>

<script>
...
import usAirportData from '@/data/us-airports.js'
import euAirportData from '@/data/eu-airports.js'

export default {
  ...
  setup() {
    const usAirports = ref(usAirportData)
    const euAirports = ref(euAirportData)

    return { usAirports, euAirports }
  }
}
</script>
...

ブラウザを開き、localhost:8080にアクセスします。 ヨーロッパの空港データは、米国の空港データの下に表示されます。

これで、別のデータセットを同じコンポーネントに正常に渡すことができました。 propsを使用すると、基本的にデータを新しい名前に再割り当てし、その新しい名前を使用して子コンポーネントのデータを参照します。

この時点で、このアプリケーションはより動的になり始めています。 しかし、これをさらに集中させて再利用できるようにするためにできることはまだ他にもあります。 Vue.jsでは、slotと呼ばれるものを使用できます。 次のステップでは、HTMLをプレースホルダーに挿入するデフォルトのslotを使用してCard.vueコンポーネントを作成します。

ステップ4—スロットを使用して一般的なカードコンポーネントを作成する

スロットは、特にそのコンポーネントのHTMLが類似しているかどうかわからない場合に、再利用可能なコンポーネントを作成するための優れた方法です。 前の手順では、異なるデータを使用して別のAirportCards.vueインスタンスを作成しました。 その例では、HTMLはそれぞれ同じです。 これは、段落タグ付きのv-forループ内の<div>です。

ターミナルを開き、touchコマンドを使用して新しいファイルを作成します。 このファイルの名前はCard.vueになります。

touch src/components/Card.vue

テキストエディタで、新しいCard.vueコンポーネントを開きます。 AirportCards.vueからCSSの一部を取得し、この新しいコンポーネントに追加します。

<style>タグを作成し、次のCSSを追加します。

sfc-project / src / components / Card.vue

<style>
.card {
  border: 3px solid;
  border-radius: .5rem;
  padding: 1rem;
  margin-bottom: 1rem;
}
</style>

次に、このコンポーネントのHTMLテンプレートを作成します。 <style>タグの前に、次の<template>タグを追加します。

sfc-project / src / components / Card.vue

<template>
  <div class="card">
  
  </div>
</template>
...

<div class="card">の間に、<slot />コンポーネントを追加します。 これは、Vueによって提供されるコンポーネントです。 このコンポーネントをインポートする必要はありません。 Vue.jsによってグローバルにインポートされます。

sfc-project / src / components / Card.vue

<template>
  <div class="card">
    <slot />
  </div>
</template>

このslotは、他の場所で参照されている場合にCard.vueコンポーネントのタグの間にあるHTMLのプレースホルダーです。

ファイルを保存して終了します。

次に、AirportCards.vueコンポーネントに戻ります。 まず、作成したばかりの新しいCard.vueSFCをインポートします。

sfc-project / src / components / AirportCards.vue

...
<script>
import Card from '@/components/Card.vue'

export default {
  name: 'Airports',
  props: { ... },
  components: {
    Card
  }
}
</script>
...

あとは、<div><card>に置き換えるだけです。

/src/components/AirportCards.vue

<template>
  <div class="wrapper">
    <card v-for="airport in airports" :key="airport.abbreviation">
      <p>{{ airport.abbreviation }}</p>
      <p>{{ airport.name }}</p>
      <p>{{ airport.city }}, {{ airport.state }}</p>
    </card>
  </div>
</template>
...

Card.vueコンポーネントに<slot />があるため、<card>タグ間のHTMLがその場所に挿入され、カードに関連付けられているすべてのスタイルが保持されます。

ファイルを保存します。 localhost:8080でブラウザを開くと、以前と同じカードが見つかります。 違いは、AirportCards.vueCard.vueコンポーネントを参照するようになったことです。

スロットのパワーを表示するには、アプリケーションでApp.vueコンポーネントを開き、Card.vueコンポーネントをインポートします。

sfc-project / src / App.vue

...
<script>
...
import Card from '@/components/Card.vue'

export default {
  ...
  components: {
    AirportCards,
    Card
  },
  setup() { ... }
}
</script>
...

<template>で、<airport-cards />インスタンスの下に以下を追加します。

sfc-project / src / App.vue

<template>
  <AirportCards :airports="usAirports"/>
  <airport-cards :airports="euAirports" />
  <card>
    <p>US Airports</p>
    <p>Total: {{ usAirports.length }}</p>
  </card>
  <card>
    <p>EU Airports</p>
    <p>Total: {{ euAirports.length }}</p>
  </card>
</template>
...

ファイルを保存し、ブラウザでlocalhost:8080にアクセスします。 ブラウザは、データセット内の空港の数を表示する追加の要素をレンダリングします。

<card />タグ間のHTMLは完全に同じではありませんが、それでも汎用カードをレンダリングします。 スロットを活用する場合、この機能を使用して、さまざまな用途を持つ小さな再利用可能なコンポーネントを作成できます。

結論

このチュートリアルでは、単一ファイルコンポーネントを作成し、propsおよびslotsを使用して再利用可能なコードブロックを作成しました。 このプロジェクトでは、多数の空港カードをレンダリングするAirportCards.vueコンポーネントを作成しました。 次に、AirportCards.vueコンポーネントを、デフォルトのスロットを持つCard.vueコンポーネントに分割しました。

最終的には、動的で、さまざまな用途に使用できる多くのコンポーネントが作成されました。その一方で、コードは保守可能であり、DRYソフトウェアの原則に準拠しています。

Vueコンポーネントの詳細については、Vueドキュメントから準備することをお勧めします。 Vueのその他のチュートリアルについては、Vue.jsシリーズページを使用してWebサイトを開発する方法を確認してください。