iT邦幫忙

2024 iThome 鐵人賽

DAY 8
1
JavaScript

不會 VueUse 而被提分手的我系列 第 8

D-08 syncRef 解析與動機 — 深度解析鏡像魔法

  • 分享至 

  • xImage
  •  

今天,我深刻理解了 syncRef 的精髓,這是一種不斷追求平衡與和諧的力量,正如我在努力找回我們之間的平衡一樣。這條學習之路並不容易,但越是困難,我越能感受到成長的力量。程世社季子,等我吧,我會讓我們的心靈再次同步!
#挽回愛情的第八天
https://ithelp.ithome.com.tw/upload/images/20240921/201621155oYbj3uFZK.png

這次筆者把重點放在原始碼的解析,讓各位更進一步了解 VueUse

1. Direction 型別

type Direction = 'ltr' | 'rtl' | 'both'

這裡定義了三種方向型別:

  • 'ltr'(left-to-right):從左到右同步資料。
  • 'rtl'(right-to-left):從右到左同步資料。
  • 'both':雙向同步資料。

2. 幾種集合運算的型別定義

這些型別定義是用來進行集合操作的,用以檢查兩個型別(集合)之間的關係,這會在後面影響 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> 判斷 AB 是否有交集但不完全相等。
  • IncludeButNotEqual<A, B> 判斷 A 是否為 B 的子集,但不完全相等。
  • NotIntersect<A, B> 判斷 AB 是否沒有交集。

可以到以下範例去看看,直接看範例會更有感覺

複雜型別的推導

3. 資料轉換器的定義

interface Transform<L, R> {
  ltr: (left: L) => R
  rtl: (right: R) => L
}

Transform 是一個介面,定義了 ltrrtl 兩個轉換函式:

  • ltr:將左邊的值轉換成右邊的值。
  • rtl:將右邊的值轉換成左邊的值。

4. 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:使用之前定義的型別來決定如何轉換資料。

5. 核心邏輯: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 函式,它會根據選項設定不同的資料同步行為:

  • 接受兩個參數 leftright,分別是 Ref<L>Ref<R>,代表要同步的兩個資料。
  • options 是一個同步的選項,用來決定同步的方向、轉換函式等。
  • 預設參數會將 flush 設為 sync(同步刷新),deepfalse(不深度監控),immediatetrue(立即執行),directionboth(雙向同步)。

接著,定義了轉換函式:

  • transformLTR:左到右的轉換,默認為一個直接回傳值的函式 (v => v)
  • transformRTL:右到左的轉換,默認同樣是一個直接回傳值的函式。

補充

  ...[options]: Equal<L, R> extends true
    ? [options?: SyncRefOptions<L, R, D>]
    : [options: SyncRefOptions<L, R, D>]

這段程式碼乍看之下很可怕,這是一個條件類型,根據 LR 是否相等來決定 options 參數是否為可選:

  • 如果 LR 相等 (Equal<L, R> extends true),則 options 是可選的
  • 如果 LR 不相等,則 options 是必需的

使用 ...[] 語法是為了使 options 成為一個剩餘參數,允許函式調用時省略這個參數

這個類型定義的巧妙之處在於:

  1. 它允許在 LR 類型相同時省略 options 參數,因為此時可能不需要任何轉換函式。
  2. LR 類型不同時,強制要求提供 options,因為這種情況下可能需要轉換函式來處理類型差異。
  3. 使用 Direction 類型參數 D 允許在類型層面控制同步的方向。
  4. SyncRefOptions<L, R, D> 類型可能包含了根據 LRD 定制的選項,以處理不同類型和方向的同步需求。

6. 資料同步邏輯

  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',它會監聽右邊的資料變化,然後更新左邊的資料。

7. 停止監控器

  const stop = () => {
    watchers.forEach(w => w.stop())
  }

  return stop
}

最後,stop 函式可以停止所有的監控器。這在某些情況下很有用,比如當你不再需要資料同步時,可以使用這個函式來停止監控。

總結

原始碼除了實現雙向資料同步,還會根據不同的情況提供了多種型別檢查和選項來控制同步的行為。它允許:

  • 使用者控制資料的同步方向(左到右、右到左、雙向)。
  • 自訂資料轉換器,將左邊的資料轉換成右邊的資料或反之。
  • 支援深度監控和同步行為的配置。

功能不難,但要實現這個功能需要 typescript 型別體操、 js 基本語法與vue 的技巧,算是非常適合讓各位開發者提升自己的一個小範例,總歸一句話:vueuse 我大哥啦


上一篇
D-07 syncRef 文件說明與範例 — 來點鏡像魔法
下一篇
D-09 useCycleList 文件說明與範例 — 循環的藝術
系列文
不會 VueUse 而被提分手的我13
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言