日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
終于徹底搞懂Watch、WatchEffect了,原來功能如此強(qiáng)大!

曾經(jīng)以為自己會用 watch? 、 watchEffect 了,后來發(fā)現(xiàn)只是略懂皮毛。最近我就把 Vue3 的偵聽器全面梳理了一下,分享給大家。看看有沒有你不會的吧,一起學(xué)起來!

Watch

基本用法

當(dāng)我們需要在數(shù)據(jù)變化時(shí)執(zhí)行一些“副作用”:如更改 DOM、執(zhí)行異步操作,我們可以使用 watch 函數(shù):



watch() 一共可以接受三個(gè)參數(shù),偵聽數(shù)據(jù)源、回調(diào)函數(shù)和配置選項(xiàng)。

偵聽數(shù)據(jù)源

watch 的第一個(gè)參數(shù)可以是不同形式的“數(shù)據(jù)源”,它可以是:

  • 一個(gè) ref
  • 一個(gè)計(jì)算屬性
  • 一個(gè) getter 函數(shù)(有返回值的函數(shù))
  • 一個(gè)響應(yīng)式對象
  • 以上類型的值組成的數(shù)組
const x = ref(1)
const y = ref(1)
const doubleX = computed(() => x.value * 2)
const obj = reactive({ count: 0 })

// 單個(gè) ref
watch(x, (newValue) => {
console.log(`x is ${newValue}`)
})

// 計(jì)算屬性
watch(doubleX, (newValue) => {
console.log(`doubleX is ${newValue}`)
})

// getter 函數(shù)
watch(
() => x.value + y.value,
(sum) => {
console.log(`sum of x + y is: ${sum}`)
}
)

// 響應(yīng)式對象
watch(obj, (newValue, oldValue) => {
// 在嵌套的屬性變更時(shí)觸發(fā)
// 注意:`newValue` 此處和 `oldValue` 是相等的
// 因?yàn)樗鼈兪峭粋€(gè)對象!
})

// 以上類型的值組成的數(shù)組
watch([x, () => y.value], ([newX, newY]) => {
console.log(`x is ${newX} and y is ${newY}`)
})

注意,你不能直接偵聽響應(yīng)式對象的屬性值,例如:

const obj = reactive({ count: 0 })

// 錯(cuò)誤,因?yàn)?watch() 得到的參數(shù)是一個(gè) number
watch(obj.count, (count) => {
console.log(`count is: ${count}`)
})

這里需要用一個(gè)返回該屬性的 getter 函數(shù):

// 提供一個(gè) getter 函數(shù)
watch(
() => obj.count,
(count) => {
console.log(`count is: ${count}`)
}
)

回調(diào)函數(shù)

watch 的第二個(gè)參數(shù)是數(shù)據(jù)發(fā)生變化時(shí)執(zhí)行的回調(diào)函數(shù)。

這個(gè)回調(diào)函數(shù)接受三個(gè)參數(shù):新值、舊值,以及一個(gè)用于清理副作用的回調(diào)函數(shù)。該回調(diào)函數(shù)會在副作用下一次執(zhí)行前調(diào)用,可以用來清除無效的副作用,如等待中的異步請求:

const id = ref(1)
const data = ref(null)

watch(id, async (newValue, oldValue, onCleanup) => {
const { response, cancel } = doAsyncWork(id.value)
// `cancel` 會在 `id` 更改時(shí)調(diào)用
// 以便取消之前未完成的請求
onCleanup(cancel)
data.value = await response.json()
})

watch 的返回值是一個(gè)用來停止該副作用的函數(shù):

const unwatch = watch(() => {})
// ...當(dāng)該偵聽器不再需要時(shí)
unwatch()

注意:使用同步語句創(chuàng)建的偵聽器,會自動綁定到宿主組件實(shí)例上,并且會在宿主組件卸載時(shí)自動停止。使用異步回調(diào)創(chuàng)建一個(gè)偵聽器,則不會綁定到當(dāng)前組件上,你必須手動停止它,以防內(nèi)存泄漏。如下面這個(gè)例子:

配置選項(xiàng)

watch 的第三個(gè)參數(shù)是一個(gè)可選的對象,支持以下選項(xiàng):

  • immediate:在偵聽器創(chuàng)建時(shí)立即觸發(fā)回調(diào)。
  • deep:深度遍歷,以便在深層級變更時(shí)觸發(fā)回調(diào)。
  • flush:回調(diào)函數(shù)的觸發(fā)時(shí)機(jī)。pre:默認(rèn),dom 更新前調(diào)用,post: dom 更新后調(diào)用,sync 同步調(diào)用。
  • onTrack / onTrigger:用于調(diào)試的鉤子。在依賴收集和回調(diào)函數(shù)觸發(fā)時(shí)被調(diào)用。
  • 深層偵聽器

直接給 watch() 傳入一個(gè)響應(yīng)式對象,會默認(rèn)創(chuàng)建一個(gè)深層偵聽器 —— 所有嵌套的屬性變更時(shí)都會被觸發(fā):

const obj = reactive({ count: 0 })

watch(obj, (newValue, oldValue) => {
// 在嵌套的屬性變更時(shí)觸發(fā)
// 注意:`newValue` 此處和 `oldValue` 是相等的
// 因?yàn)樗鼈兪峭粋€(gè)對象!
})

obj.count++

相比之下,一個(gè)返回響應(yīng)式對象的 getter 函數(shù),只有在對象被替換時(shí)才會觸發(fā):

const obj = reactive({
someString: 'hello',
someObject: { count: 0 }
})

watch(
() => obj.someObject,
() => {
// 僅當(dāng) obj.someObject 被替換時(shí)觸發(fā)
}
)

當(dāng)然,你也可以顯式地加上 deep 選項(xiàng),強(qiáng)制轉(zhuǎn)成深層偵聽器:

watch(
() => obj.someObject,
(newValue, oldValue) => {
// `newValue` 此處和 `oldValue` 是相等的
// 除非 obj.someObject 被整個(gè)替換了
console.log('deep', newValue.count, oldValue.count)
},
{ deep: true }
)

obj.someObject.count++ // deep 1 1

深層偵聽一個(gè)響應(yīng)式對象或數(shù)組,新值和舊值是相等的。為了解決這個(gè)問題,我們可以對值進(jìn)行深拷貝。

watch(
() => _.cloneDeep(obj.someObject),
(newValue, oldValue) => {
// 此時(shí) `newValue` 此處和 `oldValue` 是不相等的
console.log('deep', newValue.count, oldValue.count)
},
{ deep: true }
)

obj.someObject.count++ // deep 1 0

注意:深層偵聽需要遍歷所有嵌套的屬性,當(dāng)數(shù)據(jù)結(jié)構(gòu)龐大時(shí),開銷很大。所以我們要謹(jǐn)慎使用,并且留意性能。

watchEffect

watch()? 是懶執(zhí)行的:當(dāng)數(shù)據(jù)源發(fā)生變化時(shí),才會執(zhí)行回調(diào)。但在某些場景中,我們希望在創(chuàng)建偵聽器時(shí),立即執(zhí)行一遍回調(diào)。當(dāng)然使用 immediate 選項(xiàng)也能實(shí)現(xiàn):

const url = ref('https://...')
const data = ref(null)

async function fetchData() {
const response = await fetch(url.value)
data.value = await response.json()
}

// 立即執(zhí)行一次,再偵聽 url 變化
watch(url, fetchData, { immediate: true })

可以看到 watch? 用到了三個(gè)參數(shù),我們可以用 watchEffect? 來簡化上面的代碼。watchEffect 會立即執(zhí)行一遍回調(diào)函數(shù),如果這時(shí)函數(shù)產(chǎn)生了副作用,Vue 會自動追蹤副作用的依賴關(guān)系,自動分析出偵聽數(shù)據(jù)源。上面的例子可以重寫為:

const url = ref('https://...')
const data = ref(null)

// 一個(gè)參數(shù)就可以搞定
watchEffect(async () => {
const response = await fetch(url.value)
data.value = await response.json()
})

watchEffect? 接受兩個(gè)參數(shù),第一個(gè)參數(shù)是數(shù)據(jù)發(fā)生變化時(shí)執(zhí)行的回調(diào)函數(shù),用法和 watch? 一樣。第二個(gè)參數(shù)是一個(gè)可選的對象,支持 flush? 和 onTrack / onTrigger? 選項(xiàng),功能和 watch 相同。

注意:watchEffect? 僅會在其同步執(zhí)行期間,才追蹤依賴。使用異步回調(diào)時(shí),只有在第一個(gè) await 之前訪問到的依賴才會被追蹤。

watch? vs. watchEffect

watch? 和 watchEffect 的主要功能是相同的,都能響應(yīng)式地執(zhí)行回調(diào)函數(shù)。它們的區(qū)別是追蹤響應(yīng)式依賴的方式不同:

  • watch? 只追蹤明確定義的數(shù)據(jù)源,不會追蹤在回調(diào)中訪問到的東西;默認(rèn)情況下,只有在數(shù)據(jù)源發(fā)生改變時(shí)才會觸發(fā)回調(diào);watch 可以訪問偵聽數(shù)據(jù)的新值和舊值。
  • watchEffect? 會初始化執(zhí)行一次,在副作用發(fā)生期間追蹤依賴,自動分析出偵聽數(shù)據(jù)源;watchEffect 無法訪問偵聽數(shù)據(jù)的新值和舊值。

簡單一句話,watch? 功能更加強(qiáng)大,而 watchEffect 在某些場景下更加簡潔。


文章標(biāo)題:終于徹底搞懂Watch、WatchEffect了,原來功能如此強(qiáng)大!
瀏覽路徑:http://www.dlmjj.cn/article/copccpo.html