<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
在之前的系列文章中我們介紹了vueuse對watch封裝的一系列方法,以便我們可以更高效的開發。有對回撥進行控制的watchWithFilter,有適用於當watch的值為真值時觸發回撥的whenever,還有隻觸發一次的watchOnce和最多觸發一定次數的watchAtMost。但是我們最常用的場景可能是被觀察的變數在滿足某個具體條件時則觸發回撥,今天要學習的until就是直到滿足某種條件時則觸發一次回撥函數。讓我們通過範例程式碼和原始碼來研究一下吧~
結合檔案的介紹,筆者寫了如下的demo程式碼:
<script setup lang="ts"> import { until , invoke } from '@vueuse/core' import {ref} from 'vue' const source = ref(0) invoke(async () => { await until(source).toBe(4) console.log('滿足條件了') }) const clickedFn = () => { source.value ++ } </script> <template> <div>{{source}}</div> <button @click="clickedFn"> 點選按鈕 </button> </template>
如上程式碼所示,規定了當source的值為4的時候觸發執行watch回撥函數。這裡使用到了invoke方法,我們之前接觸過,原始碼如下
export function invoke<T>(fn: () => T): T { return fn() }
給定引數fn為一個函數,invoke返回函數的執行結果。程式碼執行效果如下圖所示:
當點選次數達到4次時,列印了相應的資訊。
until程式碼較多,先看兩張預覽圖,瞭解一下其大概實現:
通過以上兩張圖片我們看到until內部定義了很多的用於判斷條件是否滿足的方法,最後返回的instance也是包含這些方法的物件。下面我們對這些方法逐個分析。
function toMatch( condition: (v: any) => boolean, { flush = 'sync', deep = false, timeout, throwOnTimeout }: UntilToMatchOptions = {}, ): Promise<T> { let stop: Function | null = null const watcher = new Promise<T>((resolve) => { stop = watch( r, (v) => { if (condition(v) !== isNot) { stop?.() resolve(v) } }, { flush, deep, immediate: true, }, ) }) const promises = [watcher] if (timeout != null) { promises.push( promiseTimeout(timeout, throwOnTimeout) .then(() => unref(r)) .finally(() => stop?.()), ) } return Promise.race(promises) }
在promise建構函式的引數函數中呼叫watch API來監聽資料來源r 。當資料來源r的新值代入到條件condition中,使得condition為true時則呼叫stop停止監聽資料來源,並將promise狀態變為成功。
promise放入promises陣列中,如果使用者傳了timeout選項則promises放入呼叫promiseTimeout返回的promise範例。最後返回的是Promise.race的結果。看一下promiseTimeout的程式碼:
export function promiseTimeout( ms: number, throwOnTimeout = false, reason = 'Timeout', ): Promise<void> { return new Promise((resolve, reject) => { if (throwOnTimeout) setTimeout(() => reject(reason), ms) else setTimeout(resolve, ms) }) }
promiseTimeout返回了一個promise, 如果throwOnTimeout為true則過ms毫秒之後則將promise變為失敗狀態,否則經過ms毫秒後呼叫resolve,使promise變為成功狀態。
function toBe<P>(value: MaybeRef<P | T>, options?: UntilToMatchOptions) { if (!isRef(value)) return toMatch(v => v === value, options) const { flush = 'sync', deep = false, timeout, throwOnTimeout } = options ?? {} let stop: Function | null = null const watcher = new Promise<T>((resolve) => { stop = watch( [r, value], ([v1, v2]) => { if (isNot !== (v1 === v2)) { stop?.() resolve(v1) } }, { flush, deep, immediate: true, }, ) }) // 和toMatch相同部分省略 }
toBe方法體大部分和toMatch相同,只是watch回撥函數不同。這裡對資料來源r和toBe的引數value進行監聽,當r的值和value的值相同時,使promise狀態為成功。注意這裡的watch使用的是偵聽多個源的情況。
function toBeTruthy(options?: UntilToMatchOptions) { return toMatch(v => Boolean(v), options) } function toBeNull(options?: UntilToMatchOptions) { return toBe<null>(null, options) } function toBeUndefined(options?: UntilToMatchOptions) { return toBe<undefined>(undefined, options) } function toBeNaN(options?: UntilToMatchOptions) { return toMatch(Number.isNaN, options) }
toBeTruthy和toBeNaN是對toMatch的封裝,toBeNull和toBeUndefined是對toBe的封裝。toBeTruthy判斷是否為真值,方法是使用Boolean建構函式後判斷引數v是否為真值。
toBeNaN判斷是否為NAN, 使用的是Number的isNaN作為判斷條件,注意toBeNaN的實現不能使用toBe, 因為tobe在做比較的時候使用的是 ‘===’這對於NaN是不成立的:
toBeNull用於判斷是否為null,toBeUndefined用於判斷是否為undefined。
function toContains( value: any, options?: UntilToMatchOptions, ) { return toMatch((v) => { const array = Array.from(v as any) return array.includes(value) || array.includes(unref(value)) }, options) }
判斷資料來源v中是否有value,Array.from把v轉換為陣列,然後使用includes方法判斷array中是否包含value。
function changed(options?: UntilToMatchOptions) { return changedTimes(1, options) } function changedTimes(n = 1, options?: UntilToMatchOptions) { let count = -1 // skip the immediate check return toMatch(() => { count += 1 return count >= n }, options) }
changed用於判斷是否改變,通過呼叫changedTimes和固定第一引數n為1實現的。changedTimes的第一個引數為監聽的資料來源改變的次數,也是通過呼叫toMatch實現的,傳給toMatch的條件是一個函數,此函數會在資料來源改變時呼叫。每呼叫一次外層作用域定義的count就會累加一次 ,注意外層作用域count變數宣告為-1, 因為時立即監聽的。
至此,until原始碼內定義的函數全部分析完畢,下圖總結了這些函數之前的呼叫關係:
原始碼中最後的返回值也值得我們說一說。
until的返回值分為兩種情況:當監聽的源資料是陣列時和不是陣列時,程式碼如下圖所示:
if (Array.isArray(unref(r))) { const instance: UntilArrayInstance<T> = { toMatch, toContains, changed, changedTimes, get not() { isNot = !isNot return this }, } return instance } else { const instance: UntilValueInstance<T, boolean> = { toMatch, toBe, toBeTruthy: toBeTruthy as any, toBeNull: toBeNull as any, toBeNaN, toBeUndefined: toBeUndefined as any, changed, changedTimes, get not() { isNot = !isNot return this }, } return instance }
我們看到資料來源時陣列時返回的方法中沒有toBeTruthy,toBeNull,toBeNaN,toBeUndefined這些用於判斷基本型別值的方法。另外需要注意的是返回的instance裡面有一個get not(){// ...}
這是使用getters, 用於獲取特定的屬性(這裡是not)。在getter裡面對isNot取反,isNot返回值為this也就是instance本身,所以讀取完not屬性後可以鏈式呼叫其他方法,如下所示:
await until(ref).not.toBeNull() await until(ref).not.toBeTruthy()
until方法用於對資料監聽,返回具有多個條件判斷函數的物件,使用者可以將條件做為這些函數的引數,當監聽的資料滿足條件則停止監聽,其本質是對watch的回撥進行封裝,並結合promise.race的一個非同步方法。本文的demo程式碼已經上傳至github, 歡迎您clone並親自體驗until的使用。
以上就是until封裝watch常用邏輯簡化程式碼寫法的詳細內容,更多關於until封裝watch邏輯的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45