Vue 3 中用 watch 监听分页页码变化自动请求数据,需确保页码响应式、设置 immediate: true、控制 loading 状态、防重复请求并做好错误处理。

在 Vue 3 中,用 watch 监听分页页码(如 currentPage)变化并自动触发接口请求,是最常见的分页联动方式。关键在于:页码是响应式数据、监听要立即执行(immediate: true)、避免重复请求(加防抖或 loading 控制)、确保请求完成后再更新数据。
1. 基础写法:watch 监听 ref 或 reactive 的页码字段
假设你用 ref 管理当前页码:
const currentPage = ref(1);
const pageSize = ref(10);
const list = ref([]);
const loading = ref(false);
// 监听 currentPage 变化,自动拉取数据
watch(
currentPage,
async (newVal, oldVal) => {
if (newVal === oldVal) return;
loading.value = true;
try {
const res = await api.getList({ page: newVal, size: pageSize.value });
list.value = res.data || [];
} catch (err) {
console.error('分页请求失败', err);
} finally {
loading.value = false;
}
},
{ immediate: true } // 页面加载时就请求第 1 页
);
2. 更稳妥的写法:监听多个依赖 + 防抖(可选)
如果页码和每页条数都可能变化(比如用户可调 pageSize),推荐监听一个计算属性或数组:
const currentPage = ref(1);
const pageSize = ref(10);
// 方式一:watch 数组(推荐用于少量确定依赖)
watch(
() => [currentPage.value, pageSize.value],
async ([page, size]) => {
loading.value = true;
try {
const res = await api.getList({ page, size });
list.value = res.data || [];
} finally {
loading.value = false;
}
},
{ immediate: true }
);
如果翻页操作频繁(比如快速点“下一页”按钮),可简单加防抖:
递归分析 Vue 项目组件依赖,从入口文件生成组件层级图,支持 Vue 2/3,输出组件名、文件路径和属性。适用于分析组件结构、排查依赖或了解项目架构。
立即学习“前端免费学习笔记(深入)”;
import { debounce } from 'lodash-es';
watch(
() => currentPage.value,
debounce(async (val) => {
loading.value = true;
const res = await api.getList({ page: val, size: pageSize.value });
list.value = res.data || [];
loading.value = false;
}, 200)
);
3. 结合 Composition API 封装成可复用逻辑(usePagination)
把分页逻辑抽成自定义 Hook,更清晰也方便复用:
// composables/usePagination.js
import { ref, watch, onBeforeUnmount } from 'vue';
import { debounce } from 'lodash-es';
export function usePagination(requestFn, options = {}) {
const {
initialPage = 1,
initialSize = 10,
autoLoad = true,
delay = 0,
} = options;
const currentPage = ref(initialPage);
const pageSize = ref(initialSize);
const list = ref([]);
const total = ref(0);
const loading = ref(false);
const abortController = ref(null);
const reload = async () => {
if (abortController.value) abortController.value.abort();
abortController.value = new AbortController();
loading.value = true;
try {
const res = await requestFn({
page: currentPage.value,
size: pageSize.value,
signal: abortController.value.signal,
});
list.value = res.list || [];
total.value = res.total || 0;
} catch (err) {
if (err.name !== 'AbortError') {
console.error('分页加载失败', err);
}
} finally {
loading.value = false;
}
};
if (autoLoad) {
const handler = delay ? debounce(reload, delay) : reload;
watch(
() => [currentPage.value, pageSize.value],
() => handler(),
{ immediate: true }
);
}
onBeforeUnmount(() => {
if (abortController.value) abortController.value.abort();
});
return {
currentPage,
pageSize,
list,
total,
loading,
reload,
};
}
在组件中使用:
import { usePagination } from '@/composables/usePagination';
import { getListApi } from '@/api/list';
export default {
setup() {
const { currentPage, pageSize, list, loading, reload } = usePagination(
(params) => getListApi(params),
{ delay: 150 }
);
const handlePageChange = (page) => {
currentPage.value = page;
};
return {
currentPage,
pageSize,
list,
loading,
handlePageChange,
};
}
};
4. 注意事项和常见坑
-
不要在 watch 回调里直接修改被监听的值,否则可能造成无限循环(比如在监听
currentPage时又给它赋新值,且没加条件判断) - 接口报错时记得置 loading = false,否则 loading 状态卡住,用户无法操作
- 服务端返回 total 后,建议同步更新分页器的总页数,避免跳转到无效页码
-
路由分页场景(如 /list?page=3)需配合
watch路由参数,而不是只监听本地 ref - 如果用的是
reactive包裹的分页对象,watch 时要传 getter 函数:watch(() => pagination.page, ...)
不复杂但容易忽略细节,核心就是:响应式页码 + 立即执行监听 + 请求状态管理 + 错误兜底。















