1
1
# コンポーネントにスロットを使用する
2
2
3
- 商品をコンポーネント化したことで、` Card ` コンポーネントに必要な情報を ` props ` で渡すだけとなり、コードが見やすくなりました 。
3
+ 商品をコンポーネント化したことで、` Card ` コンポーネントに必要な情報を ` props ` で渡すだけとなり、コードが見やすくなり、再利用性も向上しました 。
4
4
5
5
``` vue
6
6
<template>
35
35
36
36
### スロットとは
37
37
38
- Vue.js のスロットでは、親コンポーネントから子コンポーネントにコンテンツを渡してレンダリングすることが可能です。スロットを使用すると、コンポーネントの ` props ` の修正に手を入れることなく、表示するコンテンツを変更できるため、コンポーネントの再利用性と柔軟性が高まります。スロットには、** スロットコンテンツ** と** スロットアウトレット** という仕組みがあるので、説明していきます。
38
+ Vue.js のスロットでは、親コンポーネントから子コンポーネントにコンテンツを渡してレンダリングすることが可能です。\
39
+ スロットを使用するとことによって親コンポーネントからレンダリングしたいコンテンツを挿入できるため、コンポーネントの再利用性と柔軟性が高まります。
39
40
40
41
#### スロットコンテンツ
41
42
42
43
スロットコンテンツとは、子コンポーネントへ渡すコンテンツのことを指します。
43
44
44
- コンテンツを渡す方法は、親コンポーネントで子コンポーネントを呼び出し、子コンポーネントの要素へレンダリングしたいコンテンツを定義します。スロットコンテンツとして、プレーンテキスト、HTML 要素、他のコンポーネントなど、さまざまな種類を渡すことができます。
45
+ コンテンツを渡す方法は、親コンポーネントで子コンポーネントを呼び出し、子コンポーネントの要素へレンダリングしたいコンテンツを定義します。\
46
+ スロットコンテンツとして、プレーンテキスト、HTML 要素、他のコンポーネントなど、さまざまな種類を渡すことができます。
45
47
46
48
#### 親コンポーネント
47
49
@@ -54,21 +56,25 @@ Vue.js のスロットでは、親コンポーネントから子コンポーネ
54
56
#### スロットアウトレット
55
57
56
58
親コンポーネントでスロットコンテンツを定義しましたが、子コンポーネント側では、スロットコンテンツを受け取るためのスロットアウトレットを用意する必要があります。
57
-
58
- コンテンツを受け取る方法は、スロットコンテンツをレンダリングしたい場所に ` slot ` 要素を定義します。
59
+ コンテンツを受け取る方法は、スロットコンテンツをレンダリングしたい場所に ` slot ` 要素を定義します。 \
60
+ また、 ` defineSlots ` マクロを利用することで型定義を行うことができます。(親コンポーネント側でエディタの支援や型チェックを行えるようになります。)
59
61
60
62
#### 子コンポーネント
61
63
62
64
``` vue
63
- <div>
64
- <!-- 親要素のスロットコンテンツがslot要素へレンダリングされる -->
65
+ <script setup lang="ts">
66
+ defineSlots<{ default: () => any }>() // default については後述
67
+ </script>
68
+
69
+ <template>
70
+ <!-- 親要素のスロットコンテンツが slot 要素へレンダリングされる -->
65
71
<slot />
66
- </div >
72
+ </template >
67
73
```
68
74
69
75
スロットコンテンツで紹介した親コンポーネントのコードと、スロットアウトレットの子コンポーネントを組み合わせると、最終的に表示されるコードは以下のようになります。
70
76
71
- ``` vue
77
+ ``` html
72
78
<div >
73
79
<!-- スロットコンテンツがスロットアウトレットにレンダリングされている -->
74
80
スロットコンテンツ
@@ -84,6 +90,13 @@ Vue.js のスロットでは、親コンポーネントから子コンポーネ
84
90
#### 子コンポーネント
85
91
86
92
``` vue
93
+ <script setup lang="ts">
94
+ defineSlots<{
95
+ contents: () => any
96
+ footer: () => any
97
+ }>()
98
+ </script>
99
+
87
100
<template>
88
101
<div>
89
102
<h2>Child Component</h2>
@@ -104,6 +117,7 @@ Vue.js のスロットでは、親コンポーネントから子コンポーネ
104
117
<template #contents>
105
118
<p>コンテンツ</p>
106
119
</template>
120
+
107
121
<template #footer>
108
122
<p>フッター</p>
109
123
</template>
@@ -184,26 +198,8 @@ Vue.js のスロットでは、親コンポーネントから子コンポーネ
184
198
185
199
### スロットを利用し、テキストを挿入する
186
200
187
- ` Card ` コンポーネントでは、` name ` 属性に ` body ` を指定したスロットアウトレットを定義します。同時に、` props ` から ` description ` を削除しておきます。
188
-
189
- #### Card.vue / template
190
-
191
- ``` vue{9}
192
- <template>
193
- <div class="thumbnail">
194
- <img
195
- :src="image"
196
- alt="">
197
- </div>
198
- <div class="description">
199
- <h2>{{ name }}</h2>
200
- <slot name="body" />
201
- <span>¥<span class="price">{{ pricePrefix(price) }}</span></span>
202
- </div>
203
- </template>
204
- ```
205
-
206
- #### Card.vue / script
201
+ ` Card ` コンポーネントでは、` name ` 属性に ` body ` を指定したスロットアウトレットを定義します。\
202
+ ` props ` からは ` description ` を削除しておきます。
207
203
208
204
``` vue
209
205
<script setup lang="ts">
@@ -212,9 +208,24 @@ defineProps<{
212
208
image: string
213
209
name: string
214
210
price: number
211
+ description: string // [!code --]
215
212
}>()
216
- // 省略
213
+
214
+ defineSlots<{ // [!code ++]
215
+ body: () => any // [!code ++]
216
+ }>() // [!code ++]
217
217
</script>
218
+
219
+ <template>
220
+ <div class="thumbnail">
221
+ <img :src="image" alt="">
222
+ </div>
223
+ <div class="description">
224
+ <h2>{{ name }}</h2>
225
+ <slot name="body" /> <!-- [!code ++] -->
226
+ <span>¥<span class="price">{{ pricePrefix(price) }}</span></span>
227
+ </div>
228
+ </template>
218
229
```
219
230
220
231
以上で、スロットの置き換えが完了です。見た目上の変化はありませんが、` body ` のスロットコンテンツが表示されているかと思います。
@@ -231,7 +242,7 @@ defineProps<{
231
242
232
243
#### App.vue / script
233
244
234
- ``` vue{15}
245
+ ``` vue
235
246
<script setup lang="ts">
236
247
import { ref } from 'vue'
237
248
import Card from './components/Card.vue'
@@ -246,9 +257,9 @@ const items = ref([
246
257
image: '/images/item1.jpg',
247
258
soldOut: false,
248
259
selected: false,
249
- link: 'https://handson.vuejs-jp.org/'
260
+ link: 'https://handson.vuejs-jp.org/' // [!code ++]
250
261
},
251
- //省略
262
+ // 省略
252
263
])
253
264
</script>
254
265
```
@@ -260,16 +271,16 @@ const items = ref([
260
271
``` vue{10}
261
272
<template>
262
273
<!-- 省略-->
263
- <Card
264
- :id="item.id"
265
- :image="item.image"
266
- :name="item.name"
267
- :price="item.price">
268
- <template #body>
269
- <p>{{ item.description }}</p>
270
- <a v-if="item.link" :href="item.link">リンク</a>
271
- </template>
272
- </Card>
274
+ <Card
275
+ :id="item.id"
276
+ :image="item.image"
277
+ :name="item.name"
278
+ :price="item.price">
279
+ <template #body>
280
+ <p>{{ item.description }}</p>
281
+ <a v-if="item.link" :href="item.link">リンク</a> <!-- [!code ++] -- >
282
+ </template>
283
+ </Card>
273
284
<!-- 省略-->
274
285
</template>
275
286
```
@@ -286,18 +297,7 @@ const items = ref([
286
297
287
298
``` vue
288
299
<script setup lang="ts">
289
- defineProps({
290
- description: {
291
- type: String,
292
- default: '',
293
- required: false
294
- },
295
- link: {
296
- type: String,
297
- default: '',
298
- required: false
299
- }
300
- })
300
+ defineProps<{ description: string, link: string }>()
301
301
</script>
302
302
303
303
<template>
@@ -314,25 +314,25 @@ defineProps({
314
314
<script setup lang="ts">
315
315
import { ref } from 'vue'
316
316
import Card from './components/Card.vue'
317
- import CardBody from './components/CardBody.vue';
317
+ import CardBody from './components/CardBody.vue'; // [!code ++]
318
318
319
- //省略
319
+ // 省略
320
320
</script>
321
321
322
322
<template>
323
323
<!-- 省略-->
324
- <Card
325
- :id="item.id"
326
- :image="item.image"
327
- :name="item.name"
328
- :description="item.description"
329
- :price="item.price">
330
- <template #body>
331
- <CardBody
332
- :description ="item.description"
333
- :link="item.link"/ >
334
- </template>
335
- </Card>
324
+ <Card
325
+ :id="item.id"
326
+ :image="item.image"
327
+ :name="item.name"
328
+ :description="item.description"
329
+ :price="item.price">
330
+ <template #body>
331
+ <p>{{ item.description }}</p> <!-- [!code --] -->
332
+ <a v-if="item.link" :href ="item.link">リンク</a> <!-- [!code --] -->
333
+ <CardBody :description="item.description" :link="item.link" /> <!-- [!code ++] -- >
334
+ </template>
335
+ </Card>
336
336
<!-- 省略-->
337
337
</template>
338
338
```
0 commit comments