watch()
可追蹤特定 State 處理 Side Effect, 若需求是一開始要先執行 Side Effect,或同時追蹤多個 State,則可改用 Vue 3 新的 watchEffect()
。
Version
Vue 3.4
watch()
<template>
<div>Todo ID:<input v-model="todoId" /></div>
<div>
<div>ID: {{ todo.id }}</div>
<div>Title: {{ todo.title }}</div>
</div>
</template>
<script setup>
import { ref, watch } from 'vue'
let todoId = ref(1)
let todo = ref({})
watch(
todoId,
async () => {
try {
let res = await fetch(`https://jsonplaceholder.typicode.com/todos/${todoId.value}`)
todo.value = await res.json()
} catch(e) {
console.err(e)
}
},
{ immediate: true }
)
</script>
Line 2
<div>Todo ID:<input v-model="todoId" /></div>
<div>
<div>ID: {{ todo.id }}</div>
<div>Title: {{ todo.title }}</div>
</div>
- 當 user 輸入
todoId
後,下方會自動顯示該筆 todo
Line 12
let todoId = ref(1)
let todo = ref(null)
- todoId state:儲存 user 輸入的
todoId
- todo state:儲存 API 查詢所回傳的
todo
Line 15
watch(
todoId,
async () => {
try {
let res = await fetch(`https://jsonplaceholder.typicode.com/todos/${todoId.value}`)
todo.value = await res.json()
} catch (e) {
console.err(e)
}
},
{ immediate: true }
)
watch()
:todoId
:追蹤count
state,當count
state 一有變動,就會執行傳入 functionasync () => {}
:傳入 function{ immediate: true }
:Eager Watch,會立刻先執行傳入 function,而非等count
state 變動
{ immediate: true }
適合一開始以預設值
傳入 API 取得資料
watchEffect()
Async Await
<template>
<div>Todo ID:<input v-model="todoId" /></div>
<div>
<div>ID: {{ todo.id }}</div>
<div>Title: {{ todo.title }}</div>
</div>
</template>
<script setup>
import { ref, watchEffect } from 'vue'
let todoId = ref(1)
let todo = ref({})
watchEffect(async () => {
try {
let res = await fetch(`https://jsonplaceholder.typicode.com/todos/${todoId.value}`)
todo.value = await res.json()
} catch (e) {
console.error(e)
}
})
</script>
Line 15
watchEffect(async () => {
try {
let res = await fetch(`https://jsonplaceholder.typicode.com/todos/${todoId.value}`)
todo.value = await res.json()
} catch (e) {
console.error(e)
}
})
watchEffect()
:- 只需傳入 function 即可,當 function 內所使用的 state 被改變,function 會重新執行一次
- 該 function 一開始會執行一次,相當於 Eager Watch,也就是
watch()
傳入{ immediate: true }
官網有一句話很有意思:
watchEffect()
仅会在其同步
执行期间,才追踪依赖。在使用异步回调时,只有在第一个await
正常工作前访问到的属性才会被追踪
但為什麼會這樣呢?官網沒有做進一步的解釋。
Promise Chain
<template>
<div>Todo ID:<input v-model="todoId" /></div>
<div>
<div>ID: {{ todo.id }}</div>
<div>Title: {{ todo.title }}</div>
</div>
</template>
<script setup>
import { ref, watchEffect } from 'vue'
let todoId = ref(1)
let todo = ref({})
watchEffect(() => {
fetch(`https://jsonplaceholder.typicode.com/todos/${todoId.value}`)
.then((res) => res.json())
.then((data) => (todo.value = data))
.catch((e) => console.error(e))
})
</script>
Line 15
watchEffect(() => {
fetch(`https://jsonplaceholder.typicode.com/todos/${todoId.value}`)
.then((res) => res.json())
.then((data) => (todo.value = data))
.catch((e) => console.error(e))
})
- 在
then()
的部分為非同步
todoId
state 為同步
,所以會被watchEffect()
追蹤todo
state 為非同步
,所以不會被watchEffect()
追蹤
若
非同步
的todo
state 也被watchEffect()
追蹤,則傳入的 function 會被執行第二次
,甚至無限多次
,這就喪失了watchEffect()
設計的本意,因此官網才說第一個await
前的 state 才會被追蹤,之後的 state 都不被追蹤
Conclusion
watchEffect()
為 Vue 3 新提供的 Composition API,在 Options API 沒有對應寫法Async Await
只是 Promise Chain 的 Syntax Sugar,第一個await
之後的代碼都是非同步
,也就是都在then()
裡面watchEffect()
只追蹤 function 內同步
的 state,而不追蹤非同步
的 state