Vue.jsのComputed, Watchについてまとめた

## Computedとは
使用頻度がかなり高いVue.jsの算出プロパティで、基本的には既存データの加工を伴う取得に使用する。

早速、例です（[Vue.js公式](https://ja.vuejs.org/guide/essentials/computed
)から引用）
```vue
<script setup>
import { reactive, computed } from 'vue'

const author = reactive({
  name: 'John Doe',
  books: [
    'Vue 2 - Advanced Guide',
    'Vue 3 - Basic Guide',
    'Vue 4 - The Mystery'
  ]
})

// 算出プロパティの参照
const publishedBooksMessage = computed(() => {
  return author.books.length > 0 ? 'Yes' : 'No'
})
</script>

<template>
  <p>Has published books:</p>
  <span>{{ publishedBooksMessage }}</span>
</template>
```
### 特徴：
- 内部で使用しているリアクティブな値(↑の場合、author.books)を常時監視し、変更を検知したら、関数を再実行してcomputed refオブジェクトを返す。
- computed refオブジェクトは、通常のrefオブジェクトと同じように使える。
- 依存する全てのバインディングを更新（↑の場合、template内で参照してるので、computedに変更が発生するとtemplateが再レンダリングされてビューに反映される。）
- Computedは自分が使われているかどうかもわかっているので、使われてない場合は監視してる値に変更があっても関数を再実行しないでくれる。

*Computedは、内部で使用している値に変更が発生した場合に再計算が走る＝内部で使用してない値は変更があっても再計算は走らない。
<sup>新しい値をComputedで使用する前にconsoleで検出しようとしたが、再計算が動かなくてハマるケアレスミスをした事があります。

### methodとの違い
算出プロパティの代わりに、同じような関数をメソッドとして定義することもできます。

最終的には、2つのアプローチは完全に同じ結果になりますが、算出プロパティはリアクティブな依存関係にもとづき**キャッシュされる**という違いがあります。

算出プロパティは、リアクティブな依存関係が更新されたときにだけ再評価されます。

これはつまり、 author.books が変わらない限りは、publishedBooksMessage に何度アクセスしても、getter 関数を再び実行することなく、以前計算された結果を即時に返すということです。

対称的に、メソッド呼び出しは、再描画が起きると常に関数を実行します。
キャッシュさせたくない場合は、代わりにメソッドを使うのが良い。

また、算出プロパティは引数を持てないので、引数を伴う呼び出し、クリックなどのイベント処理にはメソッドを使うが良い。

### 書き込み可能なComputed (Writable Computed)
Computedはデフォルトではgetterのみの読み取り専用。算出した値を参照するだけならデフォルトのままで使えばOK

ただ、Computedの値を書き換えたい場合は、Writable Computedを使い、getter, setterを定義すれば書き込み可能なComputedを作ることができます。

以下、[Vue.js公式](https://ja.vuejs.org/guide/essentials/computed
)から引用
> Computed properties are by default getter-only. If you attempt to assign a new value to a computed property, you will receive a runtime warning. In the rare cases where you need a "writable" computed property, you can create one by providing both a getter and a setter.

#### エラーの例：
```js
const firstName = ref('John')
const nameComputed = computed(() => {
  return firstName
})

// 以下の様にnameComputedを書き換えようとすると、エラーになる
const handleOnClick = () => {
  nameComputed.value = 'David'
}
```
Vue2の場合、こんなエラーが出ます
![スクリーンショット 2024-06-09 10.36.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173182/85845d2d-b606-0e02-bda8-cba8aeb71482.png)

Vue3の場合は、Warningが出ます
![スクリーンショット 2024-06-09 13.41.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173182/39a70fc2-06c4-6ee6-50d8-9881a54cb0e0.png)

どちらもcomputed valueはreadonlyと言ってますね。


#### Writable Computedの例：
Writable Computedでget, setを設定します。
get部分はデフォルトの computed と同じ依存元の値を加工した新しい値を返す。
set部分で依存元を変更する操作を定義します。

```js
const firstName = ref('John')
const nameComputed = computed({
  // getter
  get: (): string => firstName.value,
  // setter
  set: (value: string) => {
    firstName.value = value
  },
})

// エラーにならず書き換えできる
const handleOnClick = () => {
  nameComputed.value = 'David'
}
```

#### getter関数の注意点：
算出プロパティにおける getter 関数は計算のみを行い、副作用のないものでなければならない。
以下はしないようにする。 
・他のステートを変更
・非同期リクエストを実行
・DOM を変更


### V-modelと組み合わせるとシンプルに書ける
```js
<input v-model="inputValue">

const inputValue = computed({
  // 現在の値を返す
  get: () => props.title,
  // 親コンポーネントに更新を伝える
  set: newValue => emit('update:title', newValue)
})
```

## Watchとは
データ変更の監視機能をもつ関数。
監視してるデータに変更があった際に処理を行ったり、APIリクエストを発行したりするのに適しています。色々なオプションで制御をコントロールできる。

**基本型：**
`watch(監視する変数, () => {行いたい処理})`

**変更前と、変更後の値を使う場合：**
`watch(value, (newVal, oldVal) => {行いたい処理})`
コールバック関数の第一引数に変更後の値、第二引数には変更前の値が入ります。

**サンプル(公式から拝借)**
```js
watch(question, async (newQuestion, oldQuestion) => {
  if (newQuestion.includes('?')) {
    loading.value = true
    answer.value = 'Thinking...'
    try {
      const res = await fetch('https://yesno.wtf/api')
      answer.value = (await res.json()).answer
    } catch (error) {
      answer.value = 'Error! Could not reach the API. ' + error
    } finally {
      loading.value = false
    }
  }
})
```

#### Computedとの違い
・処理の中でAPI通信など非同期処理を実行可能
・何も値を返さないでいい