Vue中watch清除過期副作用的案例詳解
在這里就不過多說watch的用法了,主要了解一下如何清除過期的副作用
通過一個(gè)案例來說吧:
一個(gè)可搜索的下拉選擇器,用戶第一次進(jìn)行搜索的時(shí)候網(wǎng)速比較慢,請(qǐng)求雖然被服務(wù)端正確響應(yīng)了,但是數(shù)據(jù)一直沒有傳輸?shù)娇蛻舳?,用戶看下拉?shù)據(jù)沒變化 就第二次搜索。第二次搜索之后網(wǎng)速恢復(fù)正常了,第二次請(qǐng)求的數(shù)據(jù)很快就客戶端接收且正確渲染;緊接著第一次的數(shù)據(jù)也被客戶端接收且客戶端正確渲染。
這樣就可能存在這樣一種情況,第一次請(qǐng)求,服務(wù)端響應(yīng)了請(qǐng)求但數(shù)據(jù)還沒被客戶端接收的時(shí)候,有人修改了數(shù)據(jù);然后用戶又點(diǎn)擊刷新,響應(yīng)數(shù)據(jù) 很快被客戶端接收且處理,這個(gè)時(shí)候已經(jīng)渲染的是最新的數(shù)據(jù)了。但是第一次請(qǐng)求的響應(yīng)數(shù)據(jù)被客戶端接收了,如果渲染的話,就不是最新的數(shù)據(jù)了。 因?yàn)榈诙握?qǐng)求被成功處理后,第一次的請(qǐng)求就已經(jīng)屬于過期的了。
// 下拉選擇器綁定的數(shù)據(jù)
const queryParams = reactive({
keyword: ''
})
// 下拉列表渲染的數(shù)據(jù)
const listData = []
// 第幾次搜索
let askNum = 0
// 每次搜索用的事件
const times = [5000, 100] // 第一次一共用了5s 第二次用了0.1s
// 監(jiān)視書的信息
watch(queryParams, async (newV) => {
// 3.查詢參數(shù)第一次發(fā)生變化,響應(yīng)很慢需要5s
// 6.查詢參數(shù)第二次發(fā)生變化,響應(yīng)非???
asyncTaskIsExpired(times[askNum++], askNum)
.then(() => {
console.log(`第${askNum}個(gè)任務(wù)執(zhí)行完畢`)
// 渲染列表數(shù)據(jù)
renderSelectData()
})
})
/*
* @param time // 任務(wù)需要的時(shí)間
* @param count // 第幾個(gè)任務(wù)
*/
function asyncTaskIsExpired(time, count) {
return new Promise((resolve) => {
console.log(`第${count}個(gè)任務(wù)開始了`)
setTimeout(() => {
resolve()
}, time)
})
}
const changeParams = function (str) {
queryParams.keyword = str
}
function renderSelectData () {
// do something
console.log('渲染數(shù)據(jù)')
}
// 第一次搜索
changeBook('xxx')
// 第一次搜索過了兩秒還沒有返回?cái)?shù)據(jù)
setTimeout(() => {
// 就開始了第二次搜索
changeBook('yyy')
}, 2000)更新后的邏輯:
// watch的第三個(gè)參數(shù),可以注冊(cè)一個(gè)過期回調(diào),當(dāng)這個(gè)副作用函數(shù)的執(zhí)行過期時(shí)將標(biāo)識(shí)修改為true
// 換句話說,就是在watch內(nèi)部每次檢測(cè)到變化時(shí),在副作用函數(shù)執(zhí)行之前,會(huì)先執(zhí)行通過onValidate注冊(cè)的回調(diào)
watch(book, async (newV, oldV, onInvalidate) => {
// 添加一個(gè)變量,標(biāo)識(shí)上一次的請(qǐng)求是否過期
let expired = false // 默認(rèn)是不過期的
onInvalidate(() => {
// 過期時(shí),將expired設(shè)置為true
console.log('副作用函數(shù)已過期')
expired = true
})
// asyncTask(times[askNum++], askNum)
asyncTaskIsExpired(times[askNum++], askNum)
.then(() => {
if (!expired) {
console.log(`第${askNum}個(gè)任務(wù)執(zhí)行完畢`)
renderSelectData()
}
})
})更新后執(zhí)行流程就是: 第一次搜索數(shù)據(jù)一直沒有返回,用戶就進(jìn)行第二次搜索,第二次搜索很快就響應(yīng)了,客戶端在處理第二次搜索響應(yīng)的數(shù)據(jù)之前,先將上一次的expired修改 為true,表示上一次請(qǐng)求已經(jīng)過期,上一次請(qǐng)求即使成功(同時(shí)發(fā)起多個(gè)請(qǐng)求的時(shí)候),也不會(huì)執(zhí)行后續(xù)操作了。
前面為什么說是“上一次的expired”呢,看上面代碼,expired實(shí)際上是存在于一個(gè)閉包中的,因此修改expired不會(huì)影響到本次請(qǐng)求。
onInvalidate的實(shí)現(xiàn)
// 遞歸讀取對(duì)象中的數(shù)據(jù)(讓對(duì)象中的所有key都有對(duì)應(yīng)副作用函數(shù))
// 從而保證修改響應(yīng)式對(duì)象的任意屬性watch都能監(jiān)聽得到然后執(zhí)行副作用函數(shù)
function traverse (value, seen = new Set()) {
// 如果要讀取的數(shù)據(jù)是原始值或者已經(jīng)讀取過了,那么什么都不做
if (typeof value !== 'object' || value !== null || seen.has(value)) return
// 將數(shù)據(jù)添加到seen中,待變遍歷地讀取過了,避免循環(huán)引用引起的死循環(huán)
seen.add(value)
// 假設(shè)value是一個(gè)對(duì)象,使用for-in讀取對(duì)象的每一個(gè)值,并遞歸調(diào)用traverse進(jìn)行處理
for (const key in value) {
traverse(value[k], seen)
}
}
function watch (source, cb, options) {
let getter
if (typeof source === 'function') {
getter = source
} else {
// 如果watch監(jiān)聽的是一個(gè)對(duì)象,讀取對(duì)象的所有屬性
getter = () => traverse(source)
}
let oldValue, newValue
// cleanup用來存儲(chǔ)用戶注冊(cè)的過期回調(diào)
let cleanup
function onInvalidate(fn) {
// 過期回調(diào)保存到cleanup中
cleanup = fn
}
const job = () => {
newValue = effectFn()
// 調(diào)用回調(diào)函數(shù)cb之前,如果有國(guó)企的回調(diào)就先調(diào)用過期回調(diào)
if (cleanup) {
cleanup()
}
// 將 onInvalidate 作為回調(diào)函數(shù)的第三個(gè)參數(shù),以邊用戶使用
cb(newValue, oldValue, onInvalidate)
oldValue = newValue
}
const effectFn = effect(
() => getter,
{
lazy: true,
scheduler: () => {
if (options.flush === 'post') {
const p = new Promise()
p.then(job)
} else {
job()
}
}
}
)
}這里 onInvalidate 實(shí)際上起到傳遞過期回調(diào)的作用。將程序本身應(yīng)該處理的邏輯加入到了vue中(類似插件系統(tǒng))。
到此這篇關(guān)于Vue中watch清除過期副作用的案例詳解的文章就介紹到這了,更多相關(guān)Vue watch清除過期副作用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue3?錨點(diǎn)定位的多種實(shí)現(xiàn)方式
這篇文章主要介紹了vue3?多種方法的錨點(diǎn)定位,使用?Vue?Router?導(dǎo)航守衛(wèi)可以簡(jiǎn)化導(dǎo)航邏輯、統(tǒng)一管理導(dǎo)航邏輯和進(jìn)行權(quán)限控制,但需要學(xué)習(xí)和理解相關(guān)概念,并且需要手動(dòng)編寫和管理導(dǎo)航守衛(wèi)的邏輯,本文給大家介紹的非常詳細(xì),需要的朋友可以參考下2023-07-07
Vue3中reactive變量重新賦值無法響應(yīng)的3種處理方法
這篇文章主要給大家介紹了關(guān)于Vue3中reactive變量重新賦值無法響應(yīng)的3種處理方法,在Vue3中可以使用reactive函數(shù)將一個(gè)普通對(duì)象轉(zhuǎn)換為響應(yīng)式對(duì)象,需要的朋友可以參考下2023-08-08
詳解從Vue.js源碼看異步更新DOM策略及nextTick
本篇文章主要介紹了從Vue.js源碼看異步更新DOM策略及nextTick,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一2017-10-10
Vue.js上下滾動(dòng)加載組件的實(shí)例代碼
本篇文章主要介紹了Vue.js上下滾動(dòng)加載組件的實(shí)例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-07-07

