iT邦幫忙

2025 iThome 鐵人賽

DAY 15
0
Vue.js

Vue.js 新手入門之路系列 第 15

Vue.js 新手入門之路 - Watchers

  • 分享至 

  • xImage
  •  

今天要來學習有關 watchers 的觀念,我會將它理解為 onchange 的概念,但傳統的 onchange 指支援監聽 DOM 元素變化,而 watch 可以用在任何響應式狀態上

  • 資料變化
  • 深層監聽
  • side effect

這邊使用 watch 監聽如果文字有包含 ? 就會執行 getAnswer() 函式,並使 answer 變為 yes / no

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

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

// 監聽 question 的變化
watch(question, (newQuestion) => {
  if (newQuestion.includes('?')) {
    getAnswer()
  }
})

// 非同步請求 API
async function getAnswer() {
  loading.value = true
  answer.value = 'Thinking...'
  try {
    const res = await fetch('https://yesno.wtf/api')
    const data = await res.json()
    answer.value = data.answer
  } catch (error) {
    answer.value = 'Error! Could not reach the API. ' + error
  } finally {
    loading.value = false
  }
}
</script>

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

<style scoped>
    .box{
        gap: 5px;
        text-align: center;
        display: flex;
        flex-direction: column;
        justify-content: center;
        font-size: x-large;
        border: solid;
        margin-left: 650px;
        margin-top: 5px;
        height: 200px;
        width:350px;
        padding: 35px;
    }
</style>

watch 預設情況屬於淺層監聽,當加上 deep: true 則為深層監聽,watch 就能 catch 到內部的變化

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

const user = ref({
  name: 'Tom',
  address: { city: 'Taipei' }
})

watch(user, (newVal, oldVal) => {
  console.log('user 變了!', newVal)
}, { deep: true })


function changeCity(newCity) {
  user.value.address.city = newCity
}
</script>

<template>
  <div class="box">
    <p>使用者:{{ user.name }}</p>
    <p>居住城市:{{ user.address.city }}</p>

    <input
      type="text"
      v-model="user.address.city"
      placeholder="輸入新城市"
    />
    <button @click="changeCity('Taichung')">改成 Taichung</button>
    <button @click="changeCity('Kaohsiung')">改成 Kaohsiung</button>
  </div>
</template>

<style scoped>
    .box{
        gap: 5px;
        text-align: center;
        display: flex;
        flex-direction: column;
        justify-content: center;
        font-size: x-large;
        border: solid;
        margin-left: 650px;
        margin-top: 5px;
        height: 300px;
        width:350px;
        padding: 35px;
    }
</style>

當我們把 deep: true 拿掉的話,則不會印出 -> user 變了!
https://ithelp.ithome.com.tw/upload/images/20250904/20178296J4AkXnGOek.png

因深層監聽其實是 Vue 使用遞迴走訪整個物件樹,所以較大的物件或陣列則可能會有效能不佳的狀況,若我們想要監聽某個深層屬性,則直接指定該屬性即可(註:用 . 隔開,但不支持表達式)

watch(() => user.value.address.city, (newCity, oldCity) => {
  console.log('城市改了:', newCity)
})

另一個狀況是 watch 預設是 lazy 的,也就是變化時會觸發,immediate: true 可以使建立監聽器的當下,立即 call 一次內部的程式碼

eg. 假設我們想要一開始就將將主題設為黑色,並且之後隨著 user 的喜好調整這時就可以使用

const theme = ref('dark')
watch(theme, (val) => {
  document.body.className = val
}, { immediate: true })

ref:
https://zh-hk.vuejs.org/guide/essentials/watchers.html#eager-watchers
https://ithelp.ithome.com.tw/m/articles/10204091


上一篇
Vue.js 新手入門之路 - 雙向綁定(二)
下一篇
Vue.js 新手入門之路 - Watchers (二)
系列文
Vue.js 新手入門之路17
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言