點燈坊

失くすものさえない今が強くなるチャンスよ

使用 watch() 追蹤 State 呼叫 API

Sam Xiao's Avatar 2024-02-01

除了使用 computed() 為特定 State 加工外,Vue 還提供 watch() 可追蹤特定 State 處理 Side Effect,尤其 呼叫 API 就是典型的 Side Effect。

Version

Vue 3.4

Composition API

<template>
  <div>
    Ask a yes/no question:
    <input v-model="question" :disabled="loading" />
  </div>
  <div>{{ answer }}</div>
</template>

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

let question = ref('')
let answer = ref('Questions usually contain a question mark. ;-)')
let loading = ref(false)

watch(question, async (newValue) => {
  if (newValue.includes('?')) {
    loading.value = true
    answer.value = 'Thinking...'
    try {
      let 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
    }
  } else {
    answer.value = 'Questions usually contain a question mark. ;-)'
  }
})
</script>

Line 2

<div>
  Ask a yes/no question:
  <input v-model="question" :disabled="loading" />
</div>
<div>{{ answer }}</div>
  • User 輸入問題
  • 下方顯示 API 回傳答案

Line 12

let question = ref('')
let answer = ref('Questions usually contain a question mark. ;-)')
let loading = ref(false)
  • question state:儲存問題
  • answer state:儲存 API 回傳答案
  • loading state:儲存 API 回傳狀態

Line 16

watch(question, async (newValue) => {
})
  • watch():追蹤 count state,當 count state 一有變動,就會執行傳入 function
  • 傳入 function 可接受兩個參數:newValueoldValue

Line 17

if (newValue.includes('?')) {
  loading.value = true
  answer.value = 'Thinking...'
  try {
    let 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
  }
} else {
  answer.value = 'Questions usually contain a question mark. ;-)'
}
  • 若輸入問題包含 ?,則呼叫 API 並顯示結果,否則繼續顯示 提示
  • 呼叫 API 就是典型的 Side Effect

Options API

<template>
  <div>
    Ask a yes/no question:
    <input v-model="question" :disabled="loading" />
  </div>
  <div>{{ answer }}</div>
</template>

<script>
export default {
  data: () => ({
    question: '',
    answer: 'Questions usually contain a question mark. ;-)',
    loading: false
  }),
  watch: {
    async question(newValue) {
      if (newValue.includes('?')) {
        this.loading = true
        this.answer = 'Thinking...'
        try {
          let res = await fetch('https://yesno.wtf/api')
          this.answer = (await res.json()).answer
        } catch (error) {
          this.answer = 'Error! Could not reach the API. ' + error
        } finally {
          this.loading = false
        }
      } else {
        this.answer = 'Questions usually contain a question mark. ;-)'
      }
    }
  }
}
</script>
  • 最後附上相同功能的 Options API 供參考
  • State 宣告在 data() function
  • watch() function 改成 watch Object,將要偵測的 state 以 method 命名

Conclusion

  • Watch 在 Options API 與 Composition API 下有顯著差異
  • Watch 在 Options API 是以 method 形式宣告在 watch Object 下
  • Watch 在 Composition API 是以 function 傳入 watch()

Reference

Vue, 偵聽器