Vue.jsのrefとreactiveを実コードで比較してみた

# 初めに
本記事は[リンクアンドモチベーション Advent Calendar 2023](https://qiita.com/advent-calendar/2023/lmi)の5日目になります。

先日、弊社がプラチナスポンサーをしていた[Vue Fes 2023](https://vuefes.jp/2023/)が開催されました。
私はブース運営で現地に赴き、実際に幾つかの講演も拝見する機会をいただきました。

その中の最後のセッションのVue.js クリニックにて、
「refとreactiveどちらをどう使い分ければいいの？」という質問があり、
聞いていて「なるほど！」ととても学びのある内容だったので、
これを機に実際に動かしながら当日エキスパートの方々が言っていた意味を更に深く理解しようと思います。

# 実際の質問とその回答
当日の[質問](https://liveq.page/ja/bloAAOkr4QG7HCKNuNXJ)とその回答を記載します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/539913/aa88f9aa-fa38-c300-b2ce-cd845f411d50.png)

- ref
    - 基本はこちらを使うでいい
        - プリミティブ型で定義出来るからチームでコードを管理するのが楽
        - 最初は皆`.value`を付けるのに慣れないと思ってたけど、今は皆慣れてるから
    - 一応、refは内部でreactiveを呼んでるとのこと([あとから調べてみた](https://github.com/vuejs/core/blob/v3.3.9/packages/reactivity/src/ref.ts#L160))
- reactive
    - Vue2と書き方が似ていて移行しやすいと思ったから用意してた
    - 公式Documentにも最初はこっちが上に書いてあったらしい
    - 2つのオブジェクトで共通のステートを持っておきたいというときイミュータブルにしたい時に便利かもしれない
    - Vue3になってthisを付けなくてもアクセスできるから普通のオブジェクトと判別がしにくい

ということで、基本はref推しでした。ドキュメントに記載する順番の話とか面白いですね。
今日は実際にコードを書いてみて、エキスパートの発言の意味することをちゃんと理解しようと試みます。

# 理解するに当たって作るアプリ
簡単な出品アプリ。名前と価格を入れたら下に表示されるだけ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/539913/16592ac9-da94-26a8-8e97-8a4fc7298bed.png)

# ref
```ref.vue
<template>
  <div>
    <h2>商品を出品する</h2>
    <form @submit.prevent="addProduct">
      <div>
        <label for="productName">商品名:</label>
        <input id="productName" :value="productName" @input="updateName">
      </div>
      <div>
        <label for="productPrice">価格:</label>
        <input id="productPrice" type="number" :value="productPrice" @input="updatePrice">
      </div>
      <button type="submit">商品を追加</button>
    </form>

    <h2>出品された商品</h2>
    <div v-for="product in products" :key="product.id">
      <h3>{{ product.name }}</h3>
      <p>価格: {{ product.price }}円</p>
    </div>
  </div>
</template>

<script>
import { ref } from 'vue';

export default {
  setup() {
    const products = ref([]);
    const productName = ref('');
    const productPrice = ref(0);

    const updateName = (event) => {
      productName.value = event.target.value;
    };

    const updatePrice = (event) => {
      productPrice.value = parseFloat(event.target.value) || 0;
    };

    const addProduct = () => {
      if (productName.value && productPrice.value > 0) {
        products.value.push({
          id: Math.random(),
          name: productName.value,
          price: productPrice.value
        });
        productName.value = '';
        productPrice.value = 0;
      }
    };

    return { products, productName, productPrice, addProduct, updateName, updatePrice };
  }
};
</script>
```

# reactive

```reactive.vue
<template>
  <div>
    <h2>商品を出品する</h2>
    <form @submit.prevent="addProduct">
      <div>
        <label for="productName">商品名:</label>
        <input id="productName" :value="newProduct.name" @input="updateName">
      </div>
      <div>
        <label for="productPrice">価格:</label>
        <input id="productPrice" type="number" :value="newProduct.price" @input="updatePrice">
      </div>
      <button type="submit">商品を追加</button>
    </form>

    <h2>出品された商品</h2>
    <div v-for="product in products" :key="product.id">
      <h3>{{ product.name }}</h3>
      <p>価格: {{ product.price }}円</p>
    </div>
  </div>
</template>

<script>
import { reactive } from 'vue';

export default {
  setup() {
    const products = reactive([]);

    const newProduct = reactive({
      name: '',
      price: 0
    });

    const updateName = (event) => {
      newProduct.name = event.target.value;
    };

    const updatePrice = (event) => {
      newProduct.price = parseFloat(event.target.value) || 0;
    };

    const addProduct = () => {
      if (newProduct.name && newProduct.price > 0) {
        products.push({
          id: Math.random(),
          name: newProduct.name,
          price: newProduct.price
        });
        newProduct.name = '';
        newProduct.price = 0;
      }
    };

    return { products, newProduct, addProduct, updateName, updatePrice };
  }
};
</script>
```

# 差分比較
## template
ここは優位な差がなかった。
refはscript部分では`.value`を付ける必要があるけど、
template部分では要らないからrefのデメリットがないからかな。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/539913/c0927af9-3b65-34aa-c24b-ff44d07a0898.png)

## script
当たり前だけど、割と差分ありますね。
冒頭に述べたエキスパートの言ってたポイントを振り返りながら話してみると
- ref
  - プリミティブ型で定義出来るからチームでコードを管理するのが楽
    - 影響が閉じられているので、変更を追いやすいなと感じました
  - 最初は皆`.value`を付けるのに慣れないと思ってたけど、今は皆慣れてるから
    - 確かにもう見慣れた。リアクティブなことが分かるから良い
- reactive
    - Vue3になってthisを付けなくてもアクセスできるから普通のオブジェクトと判別がしにくい
        - 確かに分かりにくい
    - 2つのオブジェクトで共通のステートを持っておきたいというときイミュータブルにしたい時に便利かもしれない
        - この程度だとやっぱりrefで良いじゃんってなりますね笑

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/539913/b583e84e-486a-0248-198e-3c9890037bcd.png)

# 終わりに
同じアプリをref/reactiveの別パターンで書いて比較して理解が深まった気がします。