# data を定義し、商品をレンダリングする

# 画像ファイルの配置

まず、ハンズオンで使う商品の画像を用意しましょう。ハンズオン用の画像ファイル(vue3-lab-handson-images.zip)をダウンロードおよび解凍します。

あらかじめ作成しておいた Vue.js プロジェクトを開き、 public ディレクトリの下に images ディレクトリを新しく作成します。作成した images ディレクトリの中に、先ほど解凍した画像ファイルをすべてコピーします。

# Mustache 構文

Vue.js では、{{ }} のような Mustache 構文を使ってテキストをレンダリングできます。以下の例では script 部分の message を表示しています。message の内容が変更されると、それに応じて表示も更新されます。

<template>
  <div>{{ message }}</div>
</template>

<script setup>
import { ref } from 'vue'
const message = ref('Welcome to Vue Handson!')
</script>
import { createApp } from 'vue'
import App from './App.vue'

createApp(App).mount('#app')

出力例

Welcome to Vue Handson!

ヒント

mustache とは口髭を指す英語で、二重中括弧 {{ }} が口髭のように見えることから命名されました。

# 商品をレンダリング

プロジェクトのファイルを書き換えて、商品をレンダリングしていきましょう。まず、src/App.vue ファイルの template を次のように変更します。

変更前(template)

<template>
  <img
    alt="Vue logo"
    src="./assets/logo.png">
  <HelloWorld msg="Welcome to Your Vue.js App" />
</template>

変更後(template)

<template>
  <header class="header">
    <img
      src="/images/logo.svg"
      alt="">
    <h1>Vue.js ハンズオン</h1>
  </header>
  <main class="main">
    <div class="item">
      <div class="thumbnail">
        <img
          :src="item.image"
          alt="">
      </div>
      <div class="description">
        <h2>{{ item.name }}</h2>
        <p>{{ item.description }}</p>
        <span>¥<span class="price">{{ item.price }}</span></span>
      </div>
    </div>
  </main>
</template>

次に、src/App.vue ファイルの script を次のように変更します。

変更前(script)

<script>
import HelloWorld from './components/HelloWorld.vue'

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

変更後(script)

<script setup>
import { ref } from 'vue'
const item = ref({
    id: 1,
    name: 'アボカドディップバケット',
    description:
      '刻んだ野菜をアボカドと混ぜてディップに。こんがり焼いたバゲットとお召し上がりください。',
    price: 480,
    image: '/images/item1.jpg'
})
</script>

見た目を設定するために style タグの中身を以下のように置き換えます。

<style>
body {
  font-family: sans-serif;
  margin: 0;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

#app {
  width: 90%;
  margin: 0 5%;
  color: #242424;
}

.header {
  display: flex;
  align-content: center;
  align-items: center;
  margin-top: 40px;
  margin-bottom: 40px;
}

.header > img {
  width: 100px;
  height: 100px;
  margin-right: 20px;
}

.header > h1 {
  font-size: 80px;
  font-weight: bold;
  line-height: 80px;
  margin-top: 0;
  margin-bottom: 0;
}

.main {
  display: grid;
  grid-template-columns: 3fr 3fr 3fr 3fr;
  column-gap: 24px;
  row-gap: 24px;
}

.item {
  padding: 10px;
  cursor: pointer;
}

.item:hover {
  transition: 0.2s transform ease-out;
  transform: scale(1.05);
}

.item > div.thumbnail > img {
  width: 100%;
  height: calc(100%);
  object-fit: cover;
}

.item > div.description {
  text-align: left;
  margin-top: 20px;
}

.item > div.description > p {
  margin-top: 0px;
  margin-bottom: 0px;
  font-size: 18px;
  line-height: 25px;
}

.item > div.description > span {
  display: block;
  margin-top: 10px;
  font-size: 20px;
}

.item > div.description > span > .price {
  font-size: 28px;
  font-weight: bold;
}
</style>

これで、商品を 1 つレンダリングできました。


+1 チャレンジ

ここまでの学習が完了した人は、以下の内容にも挑戦してみましょう。

# 商品の内容を変更

商品の内容を変更し、表示に反映されることを確認してみましょう。商品名 name、概要文 description、価格 price をそれぞれ適当に変更します。

<script setup>
import { ref } from 'vue'
const item = ref({
-  name: "アボカドディップバケット",
-  description: "刻んだ野菜をアボカドと混ぜてディップに。こんがり焼いたバゲットとお召し上がりください。",
-  price: 480,
+  name: "アボカドソースバケット",
+  description: "刻んだ野菜をアボカドと混ぜ、優しい味のソースに。こんがり焼いたバゲットとお召し上がりください。",
+  price: 320,
   image: "/images/item1.jpg"
})
</script>

このようにデータを更新したとき、自動的に表示が更新される状態のことを、「リアクティブ」と言います。