Vueコンポーネントのことはじめ色々

# コンポーネント化

Vueではパーツごとにコンポーネントを細かく分ける。

```vue:Item.vue
<script setup lang="ts">
import { ref } from 'vue'
import ItemPostForm from './ItemPostForm.vue';
import ItemList from './ItemList.vue';
const items = ref([{ id: 0, name: "ポーション", price: 100 }, { id: 2, name: "ハイポーション", price: 200 }])

const postItem = (name: string, price: number) => {
  const item = { id: Math.random(), name, price };
  items.value.push(item);
  inputtingName.value = "";
  inputtingPrice.value = 0;
}

const deleteItem = (id: number) => {
  items.value = items.value.filter(t => t.id != id)
}
</script>

<template>
  <div class="container">
    <h1>アイテム屋</h1>
    <ItemPostForm @post-item="postItem"/>
    <div class="item-container">
      <p v-if="items.length <= 0">Sold Out</p>
      <ul v-else>
        <ItemList :items="items" @delete-item="deleteItem"/>
      </ul>
    </div>
  </div>
</template>
```

```vue:ItemList.vue
<script setup lang="ts">
import { defineProps } from 'vue';
type Item = {
    id: number,
    name: string,
    price: number,
}

type Props = {
    items: Item[]
}

// Propsとして受け取る
// 基本的に子コンポーネントでは値を変更しない
defineProps<Props>()

const emit = defineEmits(['delete-item'])
const deleteItem = (id: number) => {
    emit('delete-item', id)
}
</script>

<template>
        <li v-for="item in items" :key="item.id" class="item-list">
          <span>{{ item.name }} {{ item.price }}</span>
          <button class="delete-button" @click="deleteItem(item.id)">delete</button>
    </li>
</template>
```

```vue:ItemPostForm.vue
<script setup lang="ts">
import { ref, defineEmits } from 'vue'
const inputtingName = ref<string>("");
const inputtingPrice = ref<number>(0);

// 子から親へのイベント伝播
const emit = defineEmits(['post-item'])
const postItem = () => {
  emit('post-item', inputtingName.value, inputtingPrice.value)
}
</script>

<template>
    <div class="form-container">
      <input v-model="inputtingName"/>
      <input v-model="inputtingPrice"/>
      <button class="save-button" @click="postItem()">post</button>
    </div>
</template>
```

## 親コンポーネントから子コンポーネントへの伝播 Props

```vue:Item.vue
<script setup lang="ts">
import { ref } from 'vue'
import ItemList from './ItemList.vue';const items = ref([{ id: 0, name: "ポーション", price: 100 }, { id: 2, name: "ハイポーション", price: 200 }])
</script>

<template>
  <div class="container">
    <h1>アイテム屋</h1>
    <ItemPostForm"/>
    <div class="item-container">
      <p v-if="items.length <= 0">Sold Out</p>
      <ul v-else>        
        <!-- v-bindで値を渡す。v-bindは省略できる。
        v-bind:itemsと同じ -->
        <ItemList :items="items"/>
      </ul>
    </div>
  </div>
</template>
```

```vue:ItemList.vue
<script setup lang="ts">
import { defineProps } from 'vue';
type Item = {
    id: number,
    name: string,
    price: number,
}

// Propsを定義
type Props = {
    items: Item[]
}

// Propsとして受け取る
// 基本的に子コンポーネントでは値を変更しない
defineProps<Props>()
</script>
```

## 子コンポーネントから親コンポーネントへの伝播 Emit

VueではEmitで伝播させる。
v-bindも利用可能だが非推奨。

```vue:Item.vue
<script setup lang="ts">
import { ref } from 'vue'
import ItemPostForm from './ItemPostForm.vue';
import ItemList from './ItemList.vue';
const items = ref([{ id: 0, name: "ポーション", price: 100 }, { id: 2, name: "ハイポーション", price: 200 }])

// javascriptではkeyと引数の値が同じであれば両略できる
// name:name, price:priceに同じ
const postItem = (name: string, price: number) => {
  const item = { id: Math.random(), name, price };
  items.value.push(item);
  inputtingName.value = "";
  inputtingPrice.value = 0;
}

const deleteItem = (id: number) => {
  items.value = items.value.filter(t => t.id !== id)
}
</script>

<template>
  <div class="container">
    <h1>アイテム屋</h1>
    <!-- postItem関数に伝播させる -->
    <ItemPostForm @post-item="postItem"/>
    <div class="item-container">
      <p v-if="items.length <= 0">Sold Out</p>
      <ul v-else>
        <!-- deleteItem関数に伝播させる -->
        <ItemList :items="items" @delete-item="deleteItem"/>
      </ul>
    </div>
  </div>
</template>
```

```vue:ItemPostForm.vue
<script setup lang="ts">
import { ref, defineEmits } from 'vue'
const inputtingName = ref<string>("");
const inputtingPrice = ref<number>(0);

// emitでイベント伝播
// 大文字は小文字に変換し、ハイフンを付ける
// postItem -> post-item
const emit = defineEmits(['post-item'])
const postItem = () => {
  emit('post-item', inputtingName.value, inputtingPrice.value)
}
</script>
```

```vue:ItemList.vue
<script setup lang="ts">
import { defineProps } from 'vue';
type Item = {
    id: number,
    name: string,
    price: number,
}

type Props = {
    items: Item[]
}
defineProps<Props>()

// emitでイベント伝播
// 大文字は小文字に変換し、ハイフンを付ける
// deleteItem -> delete-item
const emit = defineEmits(['delete-item'])
const deleteItem = (id: number) => {
    emit('delete-item', id)
}
</script>
```

# 参考
https://www.udemy.com/course/vue3-typescript/