每一頁都代表著新的內容、新的可能性,卻也意味著無盡的等待與耐心。翻頁時,我不禁思考:下一頁會帶來什麼?會不會就是我要找的?這種等待與不確定感,正是 useOffsetPagination 的精髓。無論多麼艱難,我都必須堅持下去,因為每一頁都可能是關鍵的一頁。
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
}
這裡定義了輸入和返回的結構。使用了 MaybeRef
和 MaybeRefOrGetter
,這兩個類型稍後會詳細解釋。現在只需知道,它們允許函式接受普通值、ref 或 getter 函式作為參數。
export function useOffsetPagination(options: Omit<UseOffsetPaginationOptions, 'total'>): UseOffsetPaginationInfinityPageReturn
export function useOffsetPagination(options: UseOffsetPaginationOptions): UseOffsetPaginationReturn
這裡定義了兩個函式多載,讓用戶在不提供 total
選項時可以使用無限分頁模式。
export function useOffsetPagination(options: UseOffsetPaginationOptions): UseOffsetPaginationReturn {
const {
total = Number.POSITIVE_INFINITY,
pageSize = 10,
page = 1,
onPageChange = noop,
onPageSizeChange = noop,
onPageCountChange = noop,
} = options
函式開始時進行解構並設置預設值。值得注意的是,total
的預設值設為正無窮大,這是為了配合無限分頁的模式。
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
確保 pageSize
和 page
在有效範圍內(這會在後面詳細解釋)。pageCount
是一個計算屬性,它根據總數和每頁大小計算出總頁數。
const isFirstPage = computed(() => currentPage.value === 1)
const isLastPage = computed(() => currentPage.value === pageCount.value)
這兩個計算屬性用於判斷是否為第一頁或最後一頁。
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 為唯讀,則僅進行單向同步。
function prev() {
currentPage.value--
}
function next() {
currentPage.value++
}
這兩個函式用於控制頁面的前進和後退。
watch(currentPage, () => {
onPageChange(reactive(returnValue))
})
watch(currentPageSize, () => {
onPageSizeChange(reactive(returnValue))
})
watch(pageCount, () => {
onPageCountChange(reactive(returnValue))
})
這裡使用 watch
來監聽頁碼、每頁大小和總頁數的變化,並調用相應的回調函式。
const returnValue = {
currentPage,
currentPageSize,
pageCount,
isFirstPage,
isLastPage,
prev,
next,
}
return returnValue
最後,函式返回一個包含所有必要屬性和方法的對象。
useOffsetPagination
的主要特點如下:
useClamp
確保頁碼和每頁大小在合理範圍內syncRef
實現雙向綁定,強化與外部狀態的互操作性MaybeRef<T>
是一個靈活的類型定義。它可以是 T
類型的一般的值,也可以是包裹著 T
類型值的 Ref
物件。
其定義大致如下:
type MaybeRef<T> = T | Ref<T>
使用 MaybeRef
時,需配合 unref
或 toValue
函式來獲取實際值:
import { unref } from 'vue'
function useExample(value: MaybeRef<number>) {
const actualValue = unref(value)
// 使用 actualValue...
}
當使用 MaybeRef
作為函式參數時,調用者可能無法直接看出這個參數可以是響應式的,可能需要額外的文檔說明。
MaybeRefOrGetter<T>
是 MaybeRef<T>
的擴展。它可以是 T
類型的值、Ref<T>
,或是返回 T
的函式(getter)。
其定義大致如下:
type MaybeRefOrGetter<T> = T | Ref<T> | (() => T)
使用MaybeRefOrGetter
時,需透過toValue
函式獲取實際值:
import { toValue } from '@vueuse/core'
function useExample(value: MaybeRefOrGetter<number>) {
const actualValue = toValue(value)
// 使用 actualValue...
}
toValue
函式會自動處理不同的情況:如果是普通值則直接返回,如果是 Ref 則返回 .value
,如果是函式則調用並返回結果。
使用 MaybeRefOrGetter
可能會引入額外的複雜性,因為調用者可能需要理解三種不同的傳參方式。在 API 設計時要權衡靈活性和簡單性。
useClamp
是一個實用的數值範圍限制工具。它確保數值始終保持在指定的最小值和最大值之間,自動調整超出範圍的值。這個函式支援響應式操作,並能接受多種輸入類型。在需要控制數值不超出特定範圍的場景中——例如管理頁碼、滑動條值或百分比等——useClamp
能大幅簡化代碼。它自動處理邊界情況,讓開發者無需手動編寫繁瑣的判斷邏輯。