Vue.jsコンポーネント間のpropsとemit

# はじめに
Vueコンポーネントの親と子で変数をやりとりするpropsとemitについて
配列やハッシュなどのオブジェクトをemitする場合にスプレッド構文(...numみたいなの)が出てきたりしてややこしいので、自分用にまとめてみた。

# props（親から子へデータを渡す）
+ 下記の例では3x3の多次元配列を使う。
+ セミコロンはv-bindと一緒。属性に動的な値をバインドするための省略記法。
+ v-forではkeyを設定しておかないと、要素変更された際に正しく認識できないことがある。
#### 親Parent.vue
```vue:Parent.vue
<template>
  <div>配列全体:{{arrays}}</div>
  <div v-for="(ary, i) in arrays" :key="i">
    <Child :hairetsu="ary" />
  </div>
</template>

<script setup>
import { ref } from 'vue';
import Child from './Child.vue';
const arrays = ref([
  [1,1,1],
  [2,2,2],
  [3,3,3],
]);
</script>
```
#### 子Child.vue
子コンポ―ネントでは親の配列を受け取るだけなので
definePropsだけでよい。
```vue:Child.vue
<template>
  <div>子コンポーネント{{hairetsu}}</div>
</template>
<script setup>
defineProps(["hairetsu"]);
</script>
```
### 結果

```plaintext:
配列全体:[ [ 1, 1, 1 ], [ 2, 2, 2 ], [ 3, 3, 3 ] ]
子コンポーネント:[ 1, 1, 1 ]
子コンポーネント:[ 2, 2, 2 ]
子コンポーネント:[ 3, 3, 3 ]
```
### 子コンポーネントで受け取ったdefineProps
子コンポーネント側の変数propsに入れ表示させる。
```vue:Child.vue
<template>
  <div>子コンポーネント:</div>
  <div>変数hairetsu:{{hairetsu}}</div>
  <div>変数props:{{props}}</div>
  <div>変数props.hairetsu:{{props.hairetsu}}</div>
</template>
<script setup>
const props = defineProps(["hairetsu"]);
</script>
```
### 結果
propsにいれると、definePropsで定義した変数名のハッシュになっていることがわかる。なので"props.hairetsu"としても展開できる。
```plaintext:
配列全体:[ [ 1, 1, 1 ], [ 2, 2, 2 ], [ 3, 3, 3 ] ]
子コンポーネント:
変数hairetsu:[ 1, 1, 1 ]
変数props:{ "hairetsu": [ 1, 1, 1 ] }
変数props.hairetsu:[ 1, 1, 1 ]
子コンポーネント:
変数hairetsu:[ 2, 2, 2 ]
変数props:{ "hairetsu": [ 2, 2, 2 ] }
変数props.hairetsu:[ 2, 2, 2 ]
子コンポーネント:
変数hairetsu:[ 3, 3, 3 ]
変数props:{ "hairetsu": [ 3, 3, 3 ] }
変数props.hairetsu:[ 3, 3, 3 ]
```

# 【ルール違反】子コンポーネントでテキストボックスに変数をバインドさせ、個別に値を変更
ルール違反だが、propsを介すことにより値を更新できてしまう。デベロッパツールで警告も出ない。
↓参照

https://ja.vuejs.org/guide/components/props#one-way-data-flow
>オブジェクト/配列のプロップを変更する
>
>オブジェクトや配列を props として渡した場合、子コンポーネントが props のバインディングを変更することはできませんが、オブジェクトや配列のネストされたプロパティを変更することは可能です。これは、JavaScript ではオブジェクトや配列が参照渡しであり、Vue がそのような変更を防ぐのにかかるコストが現実的でないためです。
>
>このような変更の主な欠点は、親コンポーネントにとって明瞭でない方法で子コンポーネントが親の状態に影響を与えることを許してしまい、後からデータの流れを見極めるのが難しくなる可能性があることです。親と子を密に結合させる設計でない限り、ベストプラクティスとしてはそのような変更を避けるべきです。ほとんどの場合、子コンポーネントはイベントを発行して、変更を親コンポーネントに実行してもらう必要があります。
+ 子Child.vue
```vue:Child.vue
<template>
  <input type=text v-model="props.hairetsu[0]" />
  <input type=text v-model="props.hairetsu[1]" />
  <input type=text v-model="props.hairetsu[2]" />
</template>
<script setup>
const props = defineProps(["hairetsu"]);
</script>
<style scoped>
/* みやすくするためテキストボックスに下線をつける */
input{
  border-bottom-style: solid;
  margin-right: 10px;
}
</style>
```
テキストボックスの値を変更すると親の配列の要素も変わる。
（配列内の要素が文字列になってしまうが、この辺はcomputedなどで）
![スクリーンショット 2025-02-11 21.49.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82975/0d94625a-f9de-2211-f7c3-d56eb3e5eeab.png)
# 【ルール違反】子コンポーネントでpropsで渡された配列を変更する
こちらもルール違反だができてしまう。同様に警告も出ない。
+ 子コンポーネントで配列に要素(100)を追加
```vue:Child.vue
<template>
  <div>子コンポーネント:</div>
  <div>変数hairetsu:{{hairetsu}}</div>
  <div>変数props:{{props}}</div>
  <div>変数props.hairetsu:{{props.hairetsu}}</div>
</template>
<script setup>
const props = defineProps(["hairetsu"]);
// 以下のようにhairetsuをそのまま変更することはできない
// hairestu.push(100);
// propsを通して配列の末尾に要素(100)を追加する
props.hairetsu.push(100);
</script>
```
### 結果
親の配列も動的に変更される。
```plaintext:
配列全体:[ [ 1, 1, 1, 100 ], [ 2, 2, 2, 100 ], [ 3, 3, 3, 100 ] ]
子コンポーネント:
変数hairetsu:[ 1, 1, 1, 100 ]
変数props:{ "hairetsu": [ 1, 1, 1, 100 ] }
変数props.hairetsu:[ 1, 1, 1, 100 ]
子コンポーネント:
変数hairetsu:[ 2, 2, 2, 100 ]
変数props:{ "hairetsu": [ 2, 2, 2, 100 ] }
変数props.hairetsu:[ 2, 2, 2, 100 ]
子コンポーネント:
変数hairetsu:[ 3, 3, 3, 100 ]
変数props:{ "hairetsu": [ 3, 3, 3, 100 ] }
変数props.hairetsu:[ 3, 3, 3, 100 ]
```
# emitを使って安全にデータを更新
+ 親Parent.vue
【注意点】v-forで回して子コンポーネントに配列を要素配列aryで渡していたが、emitでは変更されたものを受け取る必要があるので、親の配列そのものを渡す必要がある。（aryではなくarrays[i]を渡している）
+ v-model:hairetsuは@update:hairestuの省略記法
```vue:Parant.vue
<template>
  <div>配列全体:{{arrays}}</div>
  <div v-for="(_, i) in arrays" :key="i">
    <Child v-model:hairetsu="arrays[i]" />
  </div>
</template>

<script setup>
import { ref } from "vue";
import Child from "./Child.vue"; // 子コンポーネントをインポート

// 子コンポーネントに渡す配列データ（リアクティブ）
const arrays = ref([
  [1,1,1],
  [2,2,2],
  [3,3,3],
]);
</script>

```
+ 子Child.vue
同様の結果となるが、propで渡された配列を一旦ローカル変数にコピーし、親コンポーネントに渡しているので、処理としては安全。

```vue:Child.vue
<template>
  <input type="text" v-model="localHairetsu[0]" />
  <input type="text" v-model="localHairetsu[1]" />
  <input type="text" v-model="localHairetsu[2]" />
</template>

<script setup>
import { ref, watch } from "vue";

const props = defineProps(["hairetsu"]);
const emit = defineEmits(["update:hairetsu"]);

// props.hairetsu をローカルで管理する
const localHairetsu = ref([...props.hairetsu]);

// ローカルデータが変更されたら親に通知
watch(localHairetsu, (newVal) => {
  emit("update:hairetsu", newVal);
}, { deep: true });
</script>

<style scoped>
/* みやすくするためテキストボックスに下線をつける */
input {
  border-bottom-style: solid;
  margin-right: 10px;
}
</style>
```

# スプレッド構文
...props.hairetsuという記述があるが、これはスプレッド構文といって、配列やオブジェクトを全て、または一部分を「展開（コピー｝」するために使用される。
+ 配列のコピー
```js
const arr1 = [1, 2, 3];
const arr2 = [...arr1]; // スプレッド構文で配列arr1をコピー
arr2.push(4); 

console.log(arr1); // [1, 2, 3] → 元の配列は変更されない
console.log(arr2); // [1, 2, 3, 4] → 新しい配列に要素追加
```
+ オブジェクトのコピー
```js
const obj1 = { a: 1, b: 2 };
const obj2 = { ...obj1 }; //スプレッド構文でオブジェクトobj1をコピー

obj2.c = 3;

console.log(obj1); // { a: 1, b: 2 } → 元のオブジェクトは変更されない
console.log(obj2); // { a: 1, b: 2, c: 3 } → 新しいオブジェクトにプロパティ追加
```