Vue中watch清除過期副作用的案例詳解
在這里就不過多說watch的用法了,主要了解一下如何清除過期的副作用
通過一個(gè)案例來說吧:
一個(gè)可搜索的下拉選擇器,用戶第一次進(jìn)行搜索的時(shí)候網(wǎng)速比較慢,請求雖然被服務(wù)端正確響應(yīng)了,但是數(shù)據(jù)一直沒有傳輸?shù)娇蛻舳?,用戶看下拉?shù)據(jù)沒變化 就第二次搜索。第二次搜索之后網(wǎng)速恢復(fù)正常了,第二次請求的數(shù)據(jù)很快就客戶端接收且正確渲染;緊接著第一次的數(shù)據(jù)也被客戶端接收且客戶端正確渲染。
這樣就可能存在這樣一種情況,第一次請求,服務(wù)端響應(yīng)了請求但數(shù)據(jù)還沒被客戶端接收的時(shí)候,有人修改了數(shù)據(jù);然后用戶又點(diǎn)擊刷新,響應(yīng)數(shù)據(jù) 很快被客戶端接收且處理,這個(gè)時(shí)候已經(jīng)渲染的是最新的數(shù)據(jù)了。但是第一次請求的響應(yīng)數(shù)據(jù)被客戶端接收了,如果渲染的話,就不是最新的數(shù)據(jù)了。 因?yàn)榈诙握埱蟊怀晒μ幚砗螅谝淮蔚恼埱缶鸵呀?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ù),可以注冊一個(gè)過期回調(diào),當(dāng)這個(gè)副作用函數(shù)的執(zhí)行過期時(shí)將標(biāo)識(shí)修改為true // 換句話說,就是在watch內(nèi)部每次檢測到變化時(shí),在副作用函數(shù)執(zhí)行之前,會(huì)先執(zhí)行通過onValidate注冊的回調(diào) watch(book, async (newV, oldV, onInvalidate) => { // 添加一個(gè)變量,標(biāo)識(shí)上一次的請求是否過期 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,表示上一次請求已經(jīng)過期,上一次請求即使成功(同時(shí)發(fā)起多個(gè)請求的時(shí)候),也不會(huì)執(zhí)行后續(xù)操作了。
前面為什么說是“上一次的expired
”呢,看上面代碼,expired實(shí)際上是存在于一個(gè)閉包中的,因此修改expired不會(huì)影響到本次請求。
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ǔ)用戶注冊的過期回調(diào) let cleanup function onInvalidate(fn) { // 過期回調(diào)保存到cleanup中 cleanup = fn } const job = () => { newValue = effectFn() // 調(diào)用回調(diào)函數(shù)cb之前,如果有國企的回調(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)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue+openlayer5獲取當(dāng)前鼠標(biāo)滑過的坐標(biāo)實(shí)現(xiàn)方法
在vue項(xiàng)目中怎么獲取當(dāng)前鼠標(biāo)劃過的坐標(biāo)呢?下面通過本文給大家分享實(shí)現(xiàn)步驟,感興趣的朋友跟隨小編一起看看吧2021-11-11vue如何動(dòng)態(tài)修改$router參數(shù)
這篇文章主要介紹了vue如何動(dòng)態(tài)修改$router參數(shù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09element-ui?tree?手動(dòng)展開功能實(shí)現(xiàn)(異步樹也可以)
這篇文章主要介紹了element-ui?tree?手動(dòng)進(jìn)行展開(異步樹也可以),項(xiàng)目中用到了vue的element-ui框架,用到了el-tree組件,需要的朋友可以參考下2022-08-08vue?調(diào)用瀏覽器攝像頭實(shí)現(xiàn)及原理解析
這篇文章主要為大家介紹了vue調(diào)用瀏覽器攝像頭實(shí)現(xiàn)及原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06深入探討Vue3實(shí)現(xiàn)多數(shù)據(jù)變化監(jiān)聽的方式
隨著 Vue 3 的發(fā)布,框架帶來了更多的新特性和增強(qiáng),其中之一就是 watch 函數(shù)的升級(jí),這一改進(jìn)使得多個(gè)數(shù)據(jù)的變化偵聽更加優(yōu)雅和靈活,本文將深入探討 Vue 3 中的 watch 函數(shù),以及如何以更加優(yōu)雅的方式實(shí)現(xiàn)對(duì)多個(gè)數(shù)據(jù)變化的監(jiān)聽2023-08-08Vue3?$emit用法指南(含選項(xiàng)API、組合API及?setup?語法糖)
這篇文章主要介紹了Vue3?$emit用法指南,使用?emit,我們可以觸發(fā)事件并將數(shù)據(jù)傳遞到組件的層次結(jié)構(gòu)中,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-07-07