本章では、作成したアプリケーションに JavaScript、HTML、CSS を追加して商品を表示するページを作っていきます。 本章を実践すると、1 つの商品を表示するページが作成でき、Vue.js ではどのようにページがレンダリングされるのかが理解できます。
まず、VS Code で先ほど作成したプロジェクトを開きましょう。プロジェクトの開き方は 2 通りありますので、使いやすい方で起動して下さい。
カレントディレクトリ(プロジェクトのディレクトリ、今回の例では vue3-lab
)で以下のコマンドを実行します
code .
アプリケーションのメニューで Visual Studio Code を起動し、「ファイル」-「フォルダーを開く」で自分の環境のプロジェクトディレクトリを選択します。
次に、ハンズオンで使う商品の画像を用意しましょう。ハンズオン用の画像ファイル(vue3-lab-handson-images.zip)をダウンロードおよび解凍します。
あらかじめ作成しておいた Vue.js プロジェクトを開き、 public
ディレクトリの下に images
ディレクトリを新しく作成します。作成した images
ディレクトリの中に、先ほど解凍した画像ファイルをすべてコピーします。
ここからは、デフォルトで生成された Vue のプロジェクトを独自のコードに置き換えていきます。まず、今後のステップで利用しない不要なコードやファイルを削除していきましょう。
まず、src/main.ts
から今回は利用しない不要な css ファイルの import 削除します。
変更前(src/main.ts
)
import { createApp } from 'vue'
import App from './App.vue'
import './assets/main.css'
createApp(App).mount('#app')
変更後(src/main.ts
)
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
次に、今後のステップで利用しないファイルをディレクトリごと削除します。削除する対象は以下のディレクトリです。
src/assets/
以下すべてsrc/components/
以下すべて
::: v-pre
Vue.js では、{{ }}
のような Mustache
構文を使ってテキストをレンダリングできます。以下の例では script 部分の message を表示しています。message の内容が変更されると、それに応じて表示も更新されます。
:::
<script setup lang="ts">
import { ref } from 'vue'
const message = ref('Welcome to Vue Handson!')
</script>
<template>
<div>{{ message }}</div>
</template>
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
出力例
Welcome to Vue Handson!
::: tip ヒント
::: v-pre
mustache とは口髭を指す英語で、二重中括弧 {{ }}
が口髭のように見えることから命名されました。
:::
プロジェクトのファイルを書き換えて、商品をレンダリングしていきましょう。まず、src/App.vue
ファイルの template を次のように変更します。
変更前(template)
<template>
<header>
<img
alt="Vue logo"
class="logo"
src="./assets/logo.svg"
width="125"
height="125"
>
<div class="wrapper">
<HelloWorld msg="You did it!" />
</div>
</header>
<main>
<TheWelcome />
</main>
</template>
変更後(template)
<script setup lang="ts">
import { ref } from 'vue'
interface Item {
id: number
name: string
description: string
price: number
image: string
}
const item = ref<Item>({
id: 1,
name: 'アボカドディップバケット',
description:
'刻んだ野菜をアボカドと混ぜてディップに。こんがり焼いたバゲットとお召し上がりください。',
price: 480,
image: '/images/item1.jpg'
})
</script>
<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>
<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>
次に、src/App.vue
ファイルの script を次のように変更します。
変更前(script)
<script setup lang="ts">
import HelloWorld from './components/HelloWorld.vue'
import TheWelcome from './components/TheWelcome.vue'
</script>
変更後(script)
<script setup lang="ts">
import { ref } from 'vue'
interface Item {
id: number
name: string
description: string
price: number
image: string
}
const item = ref<Item>({
id: 1,
name: 'アボカドディップバケット',
description:
'刻んだ野菜をアボカドと混ぜてディップに。こんがり焼いたバゲットとお召し上がりください。',
price: 480,
image: '/images/item1.jpg'
})
</script>
<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>
<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>
見た目を設定するために 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 つレンダリングできました。