# 算出プロパティとウォッチ

この章では、単一ファイルコンポーネント記法を例として使用します。

# 算出プロパティ

開発中に、他の状態に依存した状態が必要となることがあります。Vue では、これをコンポーネントの算出プロパティとして処理します。算出プロパティの作成には、getter 関数を受け取り、関数の返り値に対して、イミュータブルでリアクティブな ref オブジェクトを返却する computed メソッドを利用します。。

const count = ref(1)
const plusOne = computed(() => count.value + 1)

console.log(plusOne.value) // 2

plusOne.value++ // error
1
2
3
4
5
6

または、get ならびに set 関数を用意することで、書き込み可能なオブジェクトを作成できます。

const count = ref(1)
const plusOne = computed({
  get: () => count.value + 1,
  set: val => {
    count.value = val - 1
  }
})

plusOne.value = 1
console.log(count.value) // 0
1
2
3
4
5
6
7
8
9
10

# watchEffect

リアクティブの状態に応じて、作用を 自動的に適用しなおす ために、watchEffect を利用できます。これは依存関係をリアクティブにトラッキングし、変更されるたびに即座に関数を再実行します。

const count = ref(0)

watchEffect(() => console.log(count.value))
// -> logs 0

setTimeout(() => {
  count.value++
  // -> logs 1
}, 100)
1
2
3
4
5
6
7
8
9

# 監視の停止

watchEffect をコンポーネントの setup() 関数またはライフサイクルフックの中で呼び出すと、ウォッチャはコンポーネントのライフサイクルにリンクされ、コンポーネントのアンマウント時に自動的に監視が停止します。

その他のケースのため、明示的にウォッチャによる監視を停止するための stop ハンドルが返されます:

const stop = watchEffect(() => {
  /* ... */
})

// 処理後
stop()
1
2
3
4
5
6

# 副作用の無効化

ウォッチされている関数は、それが無効化された時(つまりは、該当の作用が完了する前に状態が変化した時)にクリーンアップする必要のある非同期の関数を実行することがあります。 watchEffect による関数は、コールバックを無効化するための onInvalidate 関数を受け取ることができます。この関数は以下の場合に呼び出されます:

  • 該当の作用が再度実行された場合
  • ウォッチャが停止した場合 (例: setup() またはライフサイクルフックの中で watchEffect が使用されているコンポーネントがアンマウントされた時)
watchEffect(onInvalidate => {
  const token = performAsyncOperation(id.value)
  onInvalidate(() => {
    // ID が変更されたまたはウォッチャが停止した
    // 以前に pending となった非同期の処理を無効にする
    token.cancel()
  })
})
1
2
3
4
5
6
7
8

無効化するコールバックを直接返すのではなく、 onInvalidate 関数のコールバックを経由して登録しているのは、非同期のエラー処理では、返り値が非常に重要であるためです。データ取得を行う時、作用となる関数が非同期であることは非常に一般的なことです:

const data = ref(null)
watchEffect(async onInvalidate => {
  onInvalidate(() => {...}) // Promise の解決前にクリーンアップする関数を登録
  data.value = await fetchData(props.id)
})
1
2
3
4
5

非同期関数は暗黙的に Promise を返却しますが、 Promise が resolve される前にクリーンアップ関数を登録する必要があります。Vue は、 Promise チェーンにおける潜在的なエラーを自動的に処理するため、返却される Promise に依存しています。

# 作用フラッシュのタイミング

Vue のリアクティブシステムは、無効になった変更をバッファリングし、非同期に処理することによって、おなじ "tick" の中での複数の状態の変化に対して、不要な重複の呼び出しを避けることができています。内部的には、コンポーネントの update 関数も、監視されている作用の一つです。ユーザーによる作用がキューに入っている場合、それは常に、他の全てのコンポーネントの更新の後に呼び出されます:

<template>
  <div>{{ count }}</div>
</template>

<script>
  export default {
    setup() {
      const count = ref(0)

      watchEffect(() => {
        console.log(count.value)
      })

      return {
        count
      }
    }
  }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

この例では:

  • count は最初の実行時に同期的に記録されます。
  • count が変化した時、コンポーネントの変更後にコールバック関数が実行されます。

初回実行は、コンポーネントがマウントされる前に実行されることに注意してください。そのため、作用の中で DOM(またはテンプレートの ref)へとアクセスしたい場合は、 onMounted フック内にて実行してください:

onMounted(() => {
  watchEffect(() => {
    // DOM やテンプレートの ref へのアクセス処理
  })
})
1
2
3
4
5

ウォッチャの作用を同期的に、またはコンポーネントの更新前に再実行したい場合は、 flush オプション(デフォルト値は 'post')によって、追加 options オブジェクトとして渡すことができます:

// 同時に発火
watchEffect(
  () => {
    /* ... */
  },
  {
    flush: 'sync'
  }
)

// コンポーネントの更新前に発火
watchEffect(
  () => {
    /* ... */
  },
  {
    flush: 'pre'
  }
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# Watcher のデバッグ

onTrack および onTrigger オプションは、ウォッチャの振る舞いのデバッグに利用できます。

  • onTrack はリアクティブプロパティもしくは ref が依存関係として追跡されているときに実行されます。
  • onTrigger は、依存関係の変更によって、ウォッチャコールバック関数がトリガされているときに実行されます。

どちらのコールバックについても、問題の依存関係に関する情報を含むデバッガイベントを受け取ります。対話的に依存性を検査するために、これらのコールバックに debugger 文を記述することを推奨します:

watchEffect(
  () => {
    /* 副作用を持つ処理 */
  },
  {
    onTrigger(e) {
      debugger
    }
  }
)
1
2
3
4
5
6
7
8
9
10

onTrack および onTrigger は、開発モードでのみ動作します。

# watch

watch API は、コンポーネントのwatch プロパティと完全に同じものです。watch は特定のデータソースを監視し、別のコールバック関数内で副作用を適用する必要があります。また、デフォルトでは lazy となっています(つまり、監視しているデータソースが変更された場合に限り、コールバック関数が実行されます)。

  • watchEffect と比較して、 watch は以下を可能とします:

    • 作用の効率的な実行
    • ウォッチャの再実行条件の明文化
    • ウォッチされている状態に対しての、変更前後の値両方へのアクセス

# 単一のデータソースの監視

ウォッチャのデータソースは、 値を返す gettter 関数か、そのまま ref を渡すかになります:

// watching a getter
const state = reactive({ count: 0 })
watch(
  () => state.count,
  (count, prevCount) => {
    /* ... */
  }
)

// directly watching a ref
const count = ref(0)
watch(count, (count, prevCount) => {
  /* ... */
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 複数のデータソースの監視

ウォッチャは、配列を利用することで、複数のデータソースを同時に監視できます:

watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
  /* ... */
})
1
2
3

# watchEffect との振る舞いの共有

watch明示的な停止副作用の無効化 (代わりに第三引数に onInvalidate を渡すことになります)、作用フラッシュのタイミングならびにデバッグ手法についての挙動をwatchEffectと共有しています。

Deployed on Netlify.
最終更新日: 2/16/2021, 10:44:47 PM