iT邦幫忙

2024 iThome 鐵人賽

DAY 17
1
JavaScript

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

D-17 useOffsetPagination 解析與動機 - 翻頁的愛情

  • 分享至 

  • xImage
  •  

每一頁都代表著新的內容、新的可能性,卻也意味著無盡的等待與耐心。翻頁時,我不禁思考:下一頁會帶來什麼?會不會就是我要找的?這種等待與不確定感,正是 useOffsetPagination 的精髓。無論多麼艱難,我都必須堅持下去,因為每一頁都可能是關鍵的一頁。
https://ithelp.ithome.com.tw/upload/images/20240930/20162115KqWjOzVVDt.png

1. 類型定義

export interface UseOffsetPaginationOptions {
  total?: MaybeRefOrGetter<number>
  pageSize?: MaybeRefOrGetter<number>
  page?: MaybeRef<number>
  onPageChange?: (returnValue: UnwrapNestedRefs<UseOffsetPaginationReturn>) => unknown
  onPageSizeChange?: (returnValue: UnwrapNestedRefs<UseOffsetPaginationReturn>) => unknown
  onPageCountChange?: (returnValue: UnwrapNestedRefs<UseOffsetPaginationReturn>) => unknown
}

export interface UseOffsetPaginationReturn {
  currentPage: Ref<number>
  currentPageSize: Ref<number>
  pageCount: ComputedRef<number>
  isFirstPage: ComputedRef<boolean>
  isLastPage: ComputedRef<boolean>
  prev: () => void
  next: () => void
}

這裡定義了輸入和返回的結構。使用了 MaybeRefMaybeRefOrGetter,這兩個類型稍後會詳細解釋。現在只需知道,它們允許函式接受普通值、ref 或 getter 函式作為參數。

2. 函式多載

export function useOffsetPagination(options: Omit<UseOffsetPaginationOptions, 'total'>): UseOffsetPaginationInfinityPageReturn
export function useOffsetPagination(options: UseOffsetPaginationOptions): UseOffsetPaginationReturn

這裡定義了兩個函式多載,讓用戶在不提供 total 選項時可以使用無限分頁模式。

3. 主程式

export function useOffsetPagination(options: UseOffsetPaginationOptions): UseOffsetPaginationReturn {
  const {
    total = Number.POSITIVE_INFINITY,
    pageSize = 10,
    page = 1,
    onPageChange = noop,
    onPageSizeChange = noop,
    onPageCountChange = noop,
  } = options

函式開始時進行解構並設置預設值。值得注意的是,total 的預設值設為正無窮大,這是為了配合無限分頁的模式。

4. 核心計算邏輯

const currentPageSize = useClamp(pageSize, 1, Number.POSITIVE_INFINITY)
const pageCount = computed(() => Math.max(
  1,
  Math.ceil((toValue(total)) / toValue(currentPageSize)),
))
const currentPage = useClamp(page, 1, pageCount)

這裡使用 useClamp 確保 pageSizepage 在有效範圍內(這會在後面詳細解釋)。pageCount 是一個計算屬性,它根據總數和每頁大小計算出總頁數。

5. 輔助計算屬性

const isFirstPage = computed(() => currentPage.value === 1)
const isLastPage = computed(() => currentPage.value === pageCount.value)

這兩個計算屬性用於判斷是否為第一頁或最後一頁。

6. 同步邏輯

if (isRef(page)) {
  syncRef(page, currentPage, {
    direction: isReadonly(page) ? 'ltr' : 'both',
  })
}
if (isRef(pageSize)) {
  syncRef(pageSize, currentPageSize, {
    direction: isReadonly(pageSize) ? 'ltr' : 'both',
  })
}

這裡使用 syncRef 來同步外部傳入的 ref 和內部的 ref。若外部 ref 為唯讀,則僅進行單向同步。

7. 分頁控制函式

function prev() {
  currentPage.value--
}
function next() {
  currentPage.value++
}

這兩個函式用於控制頁面的前進和後退。

8. 監聽變化

watch(currentPage, () => {
  onPageChange(reactive(returnValue))
})
watch(currentPageSize, () => {
  onPageSizeChange(reactive(returnValue))
})
watch(pageCount, () => {
  onPageCountChange(reactive(returnValue))
})

這裡使用 watch 來監聽頁碼、每頁大小和總頁數的變化,並調用相應的回調函式。

9. 返回值

const returnValue = {
  currentPage,
  currentPageSize,
  pageCount,
  isFirstPage,
  isLastPage,
  prev,
  next,
}
return returnValue

最後,函式返回一個包含所有必要屬性和方法的對象。

總結

useOffsetPagination 的主要特點如下:

  1. 支援響應式輸入(透過 MaybeRef 和 MaybeRefOrGetter)
  2. 運用 useClamp 確保頁碼和每頁大小在合理範圍內
  3. 提供無限分頁模式
  4. 配備豐富的回調函式,便於處理分頁相關事件
  5. 採用 syncRef 實現雙向綁定,強化與外部狀態的互操作性

補充:MaybeRef

MaybeRef<T> 是一個靈活的類型定義。它可以是 T 類型的一般的值,也可以是包裹著 T 類型值的 Ref 物件。

其定義大致如下:

type MaybeRef<T> = T | Ref<T>

運用時機:

  1. 當需要參數能接受普通值和響應式引用(Ref)時。
  2. 在組件 props 定義中,允許傳入靜態值或響應式值。

注意事項:

  1. 使用 MaybeRef 時,需配合 unreftoValue 函式來獲取實際值:

    import { unref } from 'vue'
    
    function useExample(value: MaybeRef<number>) {
      const actualValue = unref(value)
      // 使用 actualValue...
    }
    
    
  2. 當使用 MaybeRef 作為函式參數時,調用者可能無法直接看出這個參數可以是響應式的,可能需要額外的文檔說明。

補充 MaybeRefOrGetter

MaybeRefOrGetter<T>MaybeRef<T> 的擴展。它可以是 T 類型的值、Ref<T>,或是返回 T 的函式(getter)。

其定義大致如下:

type MaybeRefOrGetter<T> = T | Ref<T> | (() => T)

運用時機:

  1. 當參數需要同時接受一般值、響應式引用或計算函式時。
  2. 在需要延遲計算或動態獲取值的場景中特別有用。

需要注意的點:

  1. 使用MaybeRefOrGetter時,需透過toValue函式獲取實際值:

    import { toValue } from '@vueuse/core'
    
    function useExample(value: MaybeRefOrGetter<number>) {
      const actualValue = toValue(value)
      // 使用 actualValue...
    }
    
    
  2. toValue 函式會自動處理不同的情況:如果是普通值則直接返回,如果是 Ref 則返回 .value,如果是函式則調用並返回結果。

  3. 使用 MaybeRefOrGetter 可能會引入額外的複雜性,因為調用者可能需要理解三種不同的傳參方式。在 API 設計時要權衡靈活性和簡單性。

補充 UseClamp

useClamp是一個實用的數值範圍限制工具。它確保數值始終保持在指定的最小值和最大值之間,自動調整超出範圍的值。這個函式支援響應式操作,並能接受多種輸入類型。在需要控制數值不超出特定範圍的場景中——例如管理頁碼、滑動條值或百分比等——useClamp能大幅簡化代碼。它自動處理邊界情況,讓開發者無需手動編寫繁瑣的判斷邏輯。


上一篇
D-16 useOffsetPagination 文件說明與範例 - 分分合合的資料流
下一篇
D-18 用 useOffsetPagination - 來實現電商前端分頁吧
系列文
不會 VueUse 而被提分手的我30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言