1
1
# 商品をコンポーネント化する
2
2
3
3
## 本章の概要とゴール
4
+
4
5
本章では、1 つ 1 つの商品を表示するコードをコンポーネントとして分離し、再利用できるようにプログラムを改修していきます。
5
6
本章を実践すると、プログラムの一部を再利用できるコンポーネントとして切り出したり、 ` props ` を使ってコンポーネントに必要な情報を渡すことができるようになります。
6
7
9
10
Vue.js ではテンプレート、ロジック、そしてスタイルを 1 つのファイルにまとめることで、単一ファイルコンポーネント(` Single File Components ` 、略称 ` SFC ` )として利用することができます。` SFC ` は ` <script setup> ` 内で ` import ` することで、テンプレートで直接使用することが可能となります。
10
11
11
12
``` vue{2,6}
12
- <script setup>
13
+ <script setup lang="ts" >
13
14
import MyComponent from './MyComponent.vue'
14
15
</script>
15
16
@@ -33,18 +34,19 @@ import MyComponent from './MyComponent.vue'
33
34
<<< @/../examples/event/src/App.vue#script
34
35
35
36
## 一部をモジュールとして切り出す
37
+
36
38
モジュールとして切り出す時、どの範囲で切り出すか迷うかもしれません。そのような場合は、再利用可能という観点で考えてみてもよいかもしれません。今回の場合、商品の部分は、` v-for ` の中で何度も呼ばれているので、この範囲で切り出してみるのが良さそうです。
37
39
38
40
### コンポーネントの作成
39
41
40
- ` Card ` コンポーネントとして切り出しますが、` src ` ディレクトリの下に新たに ` components ` ディレクトリを作成し、そこに ` Card.vue ` ファイルを作成します。今後コンポーネントを新たに作っていく場合は、` components ` ディレクトリに格納していくとよいでしょう。作成後は下記のようなディレクトリ構成になっていると思います。
42
+ ` Card ` コンポーネントとして切り出しますが、` src ` ディレクトリの下に新たに ` components ` ディレクトリを作成し、そこに ` Card.vue ` ファイルを作成します。今後コンポーネントを新たに作っていく場合は、` components ` ディレクトリに格納していくとよいでしょう。作成後は下記のようなディレクトリ構成になっていると思います。
41
43
42
44
```
43
45
src
44
46
┣━ components
45
47
┃ ┗━ Card.vue
46
48
┣━ App.vue
47
- ┗━ main.js
49
+ ┗━ main.ts
48
50
```
49
51
50
52
次にいよいよモジュールを切り出す作業に入ります。以下のハイライト部分を ` Card.vue ` に移します。また、` pricePrefix() ` や関連する ` style ` も一緒に移します。
66
68
#### Card.vue
67
69
68
70
``` vue
69
- <script setup>
70
- /**
71
- * 価格を3桁ごとのカンマ付きで返す
72
- * @param {number} price 価格
73
- */
74
- function pricePrefix(price) {
71
+ <script setup lang="ts">
72
+ /** 価格を3桁ごとのカンマ付きで返す */
73
+ function pricePrefix(price: number): string {
75
74
return price.toLocaleString()
76
75
}
77
76
</script>
78
77
79
78
<template>
80
79
<div class="thumbnail">
81
- <img
82
- :src="item.image"
83
- alt="">
80
+ <img :src="item.image" alt="" />
84
81
</div>
85
82
<div class="description">
86
83
<h2>{{ item.name }}</h2>
87
84
<p>{{ item.description }}</p>
88
- <span>¥<span class="price">{{ pricePrefix(item.price) }}</span></span>
85
+ <span
86
+ >¥<span class="price">{{ pricePrefix(item.price) }}</span></span
87
+ >
89
88
</div>
90
89
</template>
91
90
@@ -118,6 +117,7 @@ function pricePrefix(price) {
118
117
```
119
118
120
119
## Card コンポーネントを使用する
120
+
121
121
切り出しができたので、作成したコンポーネントを ` App.vue ` で使えるようにしましょう。
122
122
123
123
#### App.vue / template
@@ -146,7 +146,7 @@ function pricePrefix(price) {
146
146
#### App.vue / script
147
147
148
148
``` vue{3}
149
- <script setup>
149
+ <script setup lang="ts" >
150
150
import { ref } from 'vue'
151
151
import Card from './components/Card.vue'
152
152
@@ -185,42 +185,24 @@ import Card from './components/Card.vue'
185
185
#### Card.vue / script
186
186
187
187
``` vue{2-23}
188
- <script setup>
189
- defineProps({
190
- name: {
191
- type: String,
192
- default: '',
193
- required: false
194
- },
195
- description: {
196
- type: String,
197
- default: '',
198
- required: false
199
- },
200
- price: {
201
- type: Number,
202
- default: 0,
203
- required: false
204
- },
205
- image: {
206
- type: String,
207
- default: '',
208
- required: false
209
- },
210
- });
188
+ <script setup lang="ts">
189
+ defineProps<{
190
+ name: string
191
+ description: string
192
+ price: number
193
+ image: string
194
+ }>()
211
195
212
196
// 省略
213
197
214
198
</script>
215
199
```
216
200
217
- ` defineProps ` の中に受け取る ` props ` を書いていきます。` type ` は型、` default ` は初期値、` required ` は必須要素を表しています。
218
-
219
201
::: tip ヒント
220
202
` defineProps ` とこのあと紹介する ` defineEmits ` は ` <script setup> ` 内でのみ使用可能なコンパイラマクロとなっているため、` import ` する必要はありません。
221
203
:::
222
204
223
- ### App.vueから値を渡す準備をする
205
+ ### App.vue から値を渡す準備をする
224
206
225
207
` Card.vue ` の ` defineProps ` で定義した値を ` template ` 内で渡していきます。
226
208
@@ -260,6 +242,7 @@ defineProps({
260
242
` Card ` コンポーネントでは ` pricePrefix ` 関数を使用しています。このように、同じコンポーネント内で処理が完結している場合はよいですが、呼び出されている親のコンポーネントの関数を実行したい時があります。今回は例として、` Card ` コンポーネントに「売り切れ」のボタンを作成し、クリックすると非表示になる、という処理を追加してみます。
261
243
262
244
### Card コンポーネントで emits の定義をする
245
+
263
246
Vue.js では ` emits ` オプションが使えます。` emits ` オプションは、子のコンポーネント内で親のコンポーネントに発行できるイベントを定義できます。
264
247
265
248
今回では子のコンポーネントで「売り切れ」のイベントを発行して、親のコンポーネントで ` items ` を書き換える、という流れになります。現状では ` Card ` コンポーネントは渡された情報を表示するのみで、どの ` item ` か特定できる情報がないので、` id ` も渡すように修正します。` defineProps ` も忘れず修正しましょう。
@@ -278,22 +261,14 @@ Vue.js では `emits` オプションが使えます。`emits` オプション
278
261
#### Card.vue / script
279
262
280
263
``` vue{3-7}
281
- <script setup>
282
- defineProps({
283
- id: {
284
- type: Number,
285
- default: null,
286
- required: false
287
- },
288
- name: {
289
- type: String,
290
- default: '',
291
- required: false
292
- },
293
-
294
- // 省略
295
-
296
- })
264
+ <script setup lang="ts">
265
+ defineProps<{
266
+ id: number
267
+ name: string
268
+ description: string
269
+ price: number
270
+ image: string
271
+ }>()
297
272
298
273
// 省略
299
274
@@ -303,15 +278,15 @@ defineProps({
303
278
` <script setup> ` の中で ` emits ` を使用するためには ` defineEmits ` の API を使用します。` defineProps ` と同様に ` <script setup> ` の中で自動的に使えるようになっているため、` import ` は不要です。
304
279
305
280
``` vue{9}
306
- <script setup>
281
+ <script setup lang="ts" >
307
282
308
283
// 省略
309
284
310
- function pricePrefix(price) {
285
+ function pricePrefix(price: number): string {
311
286
return price.toLocaleString()
312
287
}
313
288
314
- const emit = defineEmits([ 'sold-out'] )
289
+ const emit = defineEmits<{ 'sold-out': [id: number] }>( )
315
290
</script>
316
291
```
317
292
@@ -345,11 +320,11 @@ const emit = defineEmits(['sold-out'])
345
320
` Card ` コンポーネントには ` sold-out ` の ` emits ` を受け取った場合に ` changeSoldOut ` が実行されるように設定しました。次に、実行される ` changeSoldOut ` を定義します。
346
321
347
322
``` vue{5-8}
348
- <script setup>
323
+ <script setup lang="ts" >
349
324
350
325
// 省略
351
326
352
- function changeSoldOut(id) {
327
+ function changeSoldOut(id: number ) {
353
328
const pickElm = items.value.find(item => item.id == id)
354
329
pickElm.soldOut = true
355
330
}
0 commit comments