5.0 KiB
5.0 KiB
分页 Composable 使用指南
概述
usePagination composable 是为了减少重复的分页和 URL 同步代码而创建的。它自动处理:
- 分页状态管理(页码、每页条数)
- URL 查询参数同步
- 查询条件变化时的页码重置
- 防抖和监听逻辑
基本用法
1. 简单分页(只有分页功能)
<script setup lang="ts">
import { usePagination } from "~/shared/composables/pagination"
import Pagination from "~/shared/components/Pagination.vue"
// 只使用基本分页功能
const { query } = usePagination()
// 获取数据
async function loadData() {
const offset = (query.page - 1) * query.limit
// 调用 API...
}
// 监听分页变化
watch([() => query.page, () => query.limit], loadData)
</script>
<template>
<div>
<!-- 你的数据展示 -->
<Pagination
:total="total"
v-model:page="query.page"
v-model:limit="query.limit"
/>
</div>
</template>
2. 带查询条件的分页
<script setup lang="ts">
import { usePagination } from "~/shared/composables/pagination"
interface SearchQuery {
keyword: string
status: string
category: string
}
// 定义查询条件的初始值
const { query, clearQuery } = usePagination<SearchQuery>({
keyword: "",
status: "",
category: "",
})
async function loadData() {
const offset = (query.page - 1) * query.limit
const res = await api.getData({
offset,
limit: query.limit,
keyword: query.keyword,
status: query.status,
category: query.category,
})
// 处理返回数据...
}
// 监听所有查询条件变化(会自动重置页码)
watch([
() => query.page,
() => query.limit,
() => query.keyword,
() => query.status,
() => query.category,
], loadData)
// 对于需要防抖的搜索
watchDebounced(
() => query.keyword,
loadData,
{ debounce: 500 }
)
function handleSearch() {
// 手动触发搜索,会自动重置到第一页
}
function handleClear() {
// 清空所有查询条件
clearQuery()
}
</script>
<template>
<div>
<!-- 搜索表单 -->
<n-form inline>
<n-form-item>
<n-input v-model:value="query.keyword" placeholder="搜索关键词" />
</n-form-item>
<n-form-item>
<n-select v-model:value="query.status" :options="statusOptions" />
</n-form-item>
<n-form-item>
<n-button @click="handleSearch">搜索</n-button>
<n-button @click="handleClear">清空</n-button>
</n-form-item>
</n-form>
<!-- 数据展示 -->
<!-- ... -->
<!-- 分页组件 -->
<Pagination
:total="total"
v-model:page="query.page"
v-model:limit="query.limit"
/>
</div>
</template>
配置选项
const { query } = usePagination(
{
// 查询条件初始值
keyword: "",
status: "",
},
{
defaultLimit: 20, // 默认每页条数,默认 10
defaultPage: 1, // 默认页码,默认 1
resetPageOnChange: true, // 查询条件变化时是否重置页码,默认 true
}
)
返回值说明
const {
query, // 响应式查询对象,包含 page、limit 和自定义查询条件
updateRoute, // 手动更新 URL(通常不需要调用)
resetPage, // 重置页码到第一页
clearQuery, // 清空所有查询条件
syncFromRoute // 从 URL 同步到本地状态(通常不需要调用)
} = usePagination()
迁移指南
迁移前(旧代码)
<script setup lang="ts">
const route = useRoute()
const router = useRouter()
const query = reactive({
page: parseInt(route.query.page) || 1,
limit: parseInt(route.query.limit) || 10,
keyword: route.query.keyword ?? "",
status: route.query.status ?? "",
})
function routerPush() {
router.push({
path: route.path,
query: filterEmptyValue(query),
})
}
function clear() {
query.keyword = ""
query.status = ""
}
// 大量的 watch 逻辑
watch(() => query.page, routerPush)
watch(() => [query.limit, query.status], () => {
query.page = 1
routerPush()
})
watchDebounced(() => query.keyword, () => {
query.page = 1
routerPush()
}, { debounce: 500 })
</script>
迁移后(新代码)
<script setup lang="ts">
const { query, clearQuery } = usePagination({
keyword: "",
status: "",
})
function clear() {
clearQuery()
}
// 简化的监听逻辑
watchDebounced(() => query.keyword, loadData, { debounce: 500 })
watch([() => query.page, () => query.limit, () => query.status], loadData)
</script>
优势
- 减少重复代码:URL 同步逻辑自动处理
- 统一行为:所有分页组件行为一致
- 类型安全:完整的 TypeScript 支持
- 灵活配置:支持自定义默认值和行为
- 易于维护:集中管理分页逻辑
注意事项
- 查询条件变化时会自动重置页码(可通过
resetPageOnChange: false禁用) - URL 同步是自动的,无需手动调用
router.push - 组件挂载时会自动从 URL 同步初始状态
- 支持浏览器前进后退按钮