今天,我深刻理解了 syncRef 的精髓,這是一種不斷追求平衡與和諧的力量,正如我在努力找回我們之間的平衡一樣。這條學習之路並不容易,但越是困難,我越能感受到成長的力量。程世社季子,等我吧,我會讓我們的心靈再次同步!
#挽回愛情的第八天
這次筆者把重點放在原始碼的解析,讓各位更進一步了解 VueUse
Direction 型別type Direction = 'ltr' | 'rtl' | 'both'
這裡定義了三種方向型別:
'ltr'(left-to-right):從左到右同步資料。'rtl'(right-to-left):從右到左同步資料。'both':雙向同步資料。這些型別定義是用來進行集合操作的,用以檢查兩個型別(集合)之間的關係,這會在後面影響 TransformType 的判斷:
type Equal<A, B> = [A] extends [B] ? ([B] extends [A] ? true : false) : false
type IntersectButNotEqual<A, B> = Equal<A, B> extends true ? false : A & B extends never ? false : true
type IncludeButNotEqual<A, B> = Equal<A, B> extends true ? false : A extends B ? true : false
type NotIntersect<A, B> = Equal<A, B> extends true ? false : A & B extends never ? true : false
這些定義的邏輯是:
Equal<A, B> 判斷型別 A 是否等於型別 B。IntersectButNotEqual<A, B> 判斷 A 和 B 是否有交集但不完全相等。IncludeButNotEqual<A, B> 判斷 A 是否為 B 的子集,但不完全相等。NotIntersect<A, B> 判斷 A 和 B 是否沒有交集。可以到以下範例去看看,直接看範例會更有感覺
interface Transform<L, R> {
ltr: (left: L) => R
rtl: (right: R) => L
}
Transform 是一個介面,定義了 ltr 和 rtl 兩個轉換函式:
ltr:將左邊的值轉換成右邊的值。rtl:將右邊的值轉換成左邊的值。SyncRefOptions 型別export type SyncRefOptions<L, R, D extends Direction> = ConfigurableFlushSync & {
deep?: boolean
immediate?: boolean
direction?: D
} & TransformType<D, L, R>
SyncRefOptions 定義了用來控制資料同步的選項:
deep:是否深度監控,當資料是物件或陣列時,會監控其內部的變化。immediate:是否立即執行監控器。direction:資料同步的方向,預設是 'both'。TransformType:使用之前定義的型別來決定如何轉換資料。syncRef 函式export function syncRef<L, R, D extends Direction = 'both'>(
left: Ref<L>,
right: Ref<R>,
...[options]: Equal<L, R> extends true
? [options?: SyncRefOptions<L, R, D>]
: [options: SyncRefOptions<L, R, D>]
) {
const {
flush = 'sync',
deep = false,
immediate = true,
direction = 'both',
transform = {},
} = options || {}
const watchers: WatchPausableReturn[] = []
const transformLTR = ('ltr' in transform && transform.ltr) || (v => v)
const transformRTL = ('rtl' in transform && transform.rtl) || (v => v)
這段是 syncRef 函式,它會根據選項設定不同的資料同步行為:
left 和 right,分別是 Ref<L> 和 Ref<R>,代表要同步的兩個資料。options 是一個同步的選項,用來決定同步的方向、轉換函式等。flush 設為 sync(同步刷新),deep 為 false(不深度監控),immediate 為 true(立即執行),direction 為 both(雙向同步)。接著,定義了轉換函式:
transformLTR:左到右的轉換,默認為一個直接回傳值的函式 (v => v)。transformRTL:右到左的轉換,默認同樣是一個直接回傳值的函式。 ...[options]: Equal<L, R> extends true
? [options?: SyncRefOptions<L, R, D>]
: [options: SyncRefOptions<L, R, D>]
這段程式碼乍看之下很可怕,這是一個條件類型,根據 L 和 R 是否相等來決定 options 參數是否為可選:
L 和 R 相等 (Equal<L, R> extends true),則 options 是可選的L 和 R 不相等,則 options 是必需的使用 ...[] 語法是為了使 options 成為一個剩餘參數,允許函式調用時省略這個參數
這個類型定義的巧妙之處在於:
L 和 R 類型相同時省略 options 參數,因為此時可能不需要任何轉換函式。L 和 R 類型不同時,強制要求提供 options,因為這種情況下可能需要轉換函式來處理類型差異。Direction 類型參數 D 允許在類型層面控制同步的方向。SyncRefOptions<L, R, D> 類型可能包含了根據 L、R 和 D 定制的選項,以處理不同類型和方向的同步需求。 if (direction === 'both' || direction === 'ltr') {
watchers.push(pausableWatch(
left,
(newValue) => {
watchers.forEach(w => w.pause())
right.value = transformLTR(newValue) as R
watchers.forEach(w => w.resume())
},
{ flush, deep, immediate },
))
}
if (direction === 'both' || direction === 'rtl') {
watchers.push(pausableWatch(
right,
(newValue) => {
watchers.forEach(w => w.pause())
left.value = transformRTL(newValue) as L
watchers.forEach(w => w.resume())
},
{ flush, deep, immediate },
))
}
這段程式碼根據 direction 選項設定不同方向的資料同步邏輯:
direction 是 'both' 或 'ltr',它會監聽左邊的資料變化,然後更新右邊的資料。direction 是 'both' 或 'rtl',它會監聽右邊的資料變化,然後更新左邊的資料。 const stop = () => {
watchers.forEach(w => w.stop())
}
return stop
}
最後,stop 函式可以停止所有的監控器。這在某些情況下很有用,比如當你不再需要資料同步時,可以使用這個函式來停止監控。
原始碼除了實現雙向資料同步,還會根據不同的情況提供了多種型別檢查和選項來控制同步的行為。它允許:
功能不難,但要實現這個功能需要 typescript 型別體操、 js 基本語法與vue 的技巧,算是非常適合讓各位開發者提升自己的一個小範例,總歸一句話:vueuse 我大哥啦