# Vue3 分页组件使用指南与封装方法
## 一、基础使用方法
### 1. 安装依赖
确保项目中已安装 Vue3 和 Tailwind CSS:
```bash
npm install vue@3 tailwindcss@latest
```
### 2. 创建分页组件
将前面示例代码保存为 `Pagination.vue` 文件:
```javascript
// Pagination.vue
<template>
<div class="container mx-auto px-4 py-8">
<!-- 数据列表 -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-8">
<div class="bg-white rounded-lg shadow-md p-6" v-for="item in currentPageData" :key="item.id">
<h3 class="text-xl font-semibold mb-2">{{ item.title }}</h3>
<p class="text-gray-600">{{ item.content }}</p>
</div>
</div>
<!-- 分页导航 -->
<div class="flex justify-center items-center space-x-2">
<button
@click="prevPage"
:disabled="currentPage === 1"
class="px-4 py-2 bg-gray-200 hover:bg-gray-300 rounded-md transition-colors duration-200"
>
上一页
</button>
<!-- 页码列表 -->
<div class="flex space-x-1">
<button
v-for="page in visiblePages"
:key="page"
:class="page === currentPage ? 'bg-primary text-white' : 'bg-gray-200 hover:bg-gray-300'"
@click="goToPage(page)"
class="w-10 h-10 flex items-center justify-center rounded-md transition-colors duration-200"
>
{{ page }}
</button>
</div>
<button
@click="nextPage"
:disabled="currentPage === totalPages"
class="px-4 py-2 bg-gray-200 hover:bg-gray-300 rounded-md transition-colors duration-200"
>
下一页
</button>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
// 模拟数据
const mockData = Array.from({ length: 50 }, (_, i) => ({
id: i + 1,
title: `文章标题 ${i + 1}`,
content: `这是文章 ${i + 1} 的内容描述...`
}));
// 分页状态
const currentPage = ref(1);
const itemsPerPage = ref(10);
// 计算属性
const totalItems = computed(() => mockData.length);
const totalPages = computed(() => Math.ceil(totalItems.value / itemsPerPage.value));
// 当前页数据
const currentPageData = computed(() => {
const startIndex = (currentPage.value - 1) * itemsPerPage.value;
const endIndex = startIndex + itemsPerPage.value;
return mockData.slice(startIndex, endIndex);
});
// 可见页码范围
const visiblePages = computed(() => {
let startPage = Math.max(1, currentPage.value - 2);
let endPage = Math.min(totalPages.value, startPage + 4);
if (endPage - startPage < 4) {
startPage = Math.max(1, endPage - 4);
}
return Array.from({ length: endPage - startPage + 1 }, (_, i) => startPage + i);
});
// 分页操作
const goToPage = (page) => {
if (page >= 1 && page <= totalPages.value) {
currentPage.value = page;
}
};
const prevPage = () => {
if (currentPage.value > 1) {
currentPage.value--;
}
};
const nextPage = () => {
if (currentPage.value < totalPages.value) {
currentPage.value++;
}
};
</script>
<style scoped>
.primary {
background-color: #3b82f6;
}
</style>
```
### 3. 在页面中使用
在需要分页功能的组件中引入并使用:
```javascript
<template>
<div>
<h1 class="text-2xl font-bold mb-6">文章列表</h1>
<Pagination />
</div>
</template>
<script setup>
import Pagination from './Pagination.vue';
</script>
```
## 二、组件封装方法
### 1. 提取props使组件可复用
将数据和分页配置作为 props 传入,使组件可以在不同场景复用:
```javascript
// Pagination.vue
<template>
<div class="container mx-auto px-4 py-8">
<!-- 数据列表 -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-8">
<div class="bg-white rounded-lg shadow-md p-6" v-for="item in currentPageData" :key="item.id">
<h3 class="text-xl font-semibold mb-2">{{ item.title }}</h3>
<p class="text-gray-600">{{ item.content }}</p>
</div>
</div>
<!-- 分页导航 -->
<div class="flex justify-center items-center space-x-2">
<button
@click="prevPage"
:disabled="currentPage === 1"
class="px-4 py-2 bg-gray-200 hover:bg-gray-300 rounded-md transition-colors duration-200"
>
上一页
</button>
<!-- 页码列表 -->
<div class="flex space-x-1">
<button
v-for="page in visiblePages"
:key="page"
:class="page === currentPage ? 'bg-primary text-white' : 'bg-gray-200 hover:bg-gray-300'"
@click="goToPage(page)"
class="w-10 h-10 flex items-center justify-center rounded-md transition-colors duration-200"
>
{{ page }}
</button>
</div>
<button
@click="nextPage"
:disabled="currentPage === totalPages"
class="px-4 py-2 bg-gray-200 hover:bg-gray-300 rounded-md transition-colors duration-200"
>
下一页
</button>
</div>
</div>
</template>
<script setup>
import { ref, computed, defineProps, defineEmits } from 'vue';
const props = defineProps({
data: {
type: Array,
required: true
},
itemsPerPage: {
type: Number,
default: 10
},
initialPage: {
type: Number,
default: 1
}
});
// 分页状态
const currentPage = ref(props.initialPage);
// 计算属性
const totalItems = computed(() => props.data.length);
const totalPages = computed(() => Math.ceil(totalItems.value / props.itemsPerPage));
// 当前页数据
const currentPageData = computed(() => {
const startIndex = (currentPage.value - 1) * props.itemsPerPage;
const endIndex = startIndex + props.itemsPerPage;
return props.data.slice(startIndex, endIndex);
});
// 可见页码范围
const visiblePages = computed(() => {
let startPage = Math.max(1, currentPage.value - 2);
let endPage = Math.min(totalPages.value, startPage + 4);
if (endPage - startPage < 4) {
startPage = Math.max(1, endPage - 4);
}
return Array.from({ length: endPage - startPage + 1 }, (_, i) => startPage + i);
});
// 分页操作
const goToPage = (page) => {
if (page >= 1 && page <= totalPages.value) {
currentPage.value = page;
}
};
const prevPage = () => {
if (currentPage.value > 1) {
currentPage.value--;
}
};
const nextPage = () => {
if (currentPage.value < totalPages.value) {
currentPage.value++;
}
};
</script>
<style scoped>
.primary {
background-color: #3b82f6;
}
</style>
```
### 2. 添加事件回调
添加页码变化时的回调事件,便于父组件处理:
```javascript
// 新增emits定义
const emits = defineEmits(['page-change']);
// 在页码变化时触发事件
const goToPage = (page) => {
if (page >= 1 && page <= totalPages.value) {
currentPage.value = page;
emits('page-change', page); // 触发页码变化事件
}
};
// 同样处理prevPage和nextPage方法
const prevPage = () => {
if (currentPage.value > 1) {
currentPage.value--;
emits('page-change', currentPage.value);
}
};
```
### 3. 使用插槽自定义内容
添加插槽使列表内容可以自定义:
```javascript
<template>
<div class="container mx-auto px-4 py-8">
<!-- 使用插槽显示数据 -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-8">
<slot name="item" v-for="item in currentPageData" :item="item" :key="item.id">
<!-- 默认内容 -->
<div class="bg-white rounded-lg shadow-md p-6">
<h3 class="text-xl font-semibold mb-2">{{ item.title }}</h3>
<p class="text-gray-600">{{ item.content }}</p>
</div>
</slot>
</div>
<!-- 分页导航 -->
<div class="flex justify-center items-center space-x-2">
<!-- 导航代码保持不变 -->
</div>
</div>
</template>
```
## 三、高级使用示例
### 1. 使用封装后的分页组件
```javascript
<template>
<div>
<h1 class="text-2xl font-bold mb-6">商品列表</h1>
<Pagination :data="products" :items-per-page="6">
<template #item="{ item }">
<div class="bg-white rounded-lg shadow-md overflow-hidden transition-transform duration-300 hover:scale-[1.02]">
<img :src="item.image" alt="商品图片" class="w-full h-48 object-cover">
<div class="p-4">
<h3 class="text-lg font-semibold mb-1">{{ item.name }}</h3>
<p class="text-gray-600 text-sm mb-2">{{ item.description }}</p>
<div class="flex justify-between items-center">
<span class="text-primary font-bold">¥{{ item.price }}</span>
<button class="bg-primary hover:bg-primary/90 text-white px-3 py-1 rounded text-sm transition-colors">
加入购物车
</button>
</div>
</div>
</div>
</template>
</Pagination>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import Pagination from './Pagination.vue';
const products = ref([]);
onMounted(() => {
// 模拟从API获取商品数据
fetchProducts().then(data => {
products.value = data;
});
});
const fetchProducts = async () => {
// 实际项目中这里会是API调用
return [
{ id: 1, name: '商品1', price: 99.9, description: '这是一个很棒的商品', image: 'https://picsum.photos/200/300?random=1' },
{ id: 2, name: '商品2', price: 199.9, description: '高质量产品', image: 'https://picsum.photos/200/300?random=2' },
// 更多商品...
];
};
</script>
```
### 2. 监听页码变化事件
```javascript
<Pagination
:data="products"
:items-per-page="6"
@page-change="handlePageChange"
/>
<script setup>
const handlePageChange = (page) => {
console.log('当前页码:', page);
// 可以在这里添加页面滚动到顶部等逻辑
window.scrollTo({ top: 0, behavior: 'smooth' });
};
</script>
```
## 四、自定义配置选项
### 1. 样式配置
可以通过 props 传递自定义类名:
```javascript
// 在组件中添加props
const props = defineProps({
// 其他props保持不变
pageBtnClass: {
type: String,
default: 'w-10 h-10 flex items-center justify-center rounded-md transition-colors duration-200'
},
activeBtnClass: {
type: String,
default: 'bg-primary text-white'
},
inactiveBtnClass: {
type: String,
default: 'bg-gray-200 hover:bg-gray-300'
}
});
// 在模板中使用
<button
v-for="page in visiblePages"
:key="page"
:class="[props.pageBtnClass, page === currentPage ? props.activeBtnClass : props.inactiveBtnClass]"
@click="goToPage(page)"
>
{{ page }}
</button>
```
### 2. 页码显示范围配置
```javascript
// 添加props
visibleRange: {
type: Number,
default: 2 // 左右显示的页码数量
}
// 更新计算属性
const visiblePages = computed(() => {
let startPage = Math.max(1, currentPage.value - props.visibleRange);
let endPage = Math.min(totalPages.value, startPage + props.visibleRange * 2);
if (endPage - startPage < props.visibleRange * 2) {
startPage = Math.max(1, endPage - props.visibleRange * 2);
}
return Array.from({ length: endPage - startPage + 1 }, (_, i) => startPage + i);
});
```
## 五、最佳实践建议
1. **保持组件单一职责**:分页组件只负责分页逻辑,数据获取和渲染由父组件处理
2. **使用provide/inject**:对于大型应用,可以使用 Vue 的 provide/inject 机制提供全局分页配置
3. **添加过渡动画**:使用 Vue 的 transition 组件为页面切换添加平滑过渡效果
4. **考虑无障碍性**:为分页按钮添加 aria-label 属性,提高屏幕阅读器的可用性
5. **单元测试**:为分页逻辑编写单元测试,确保各种边界条件下的正确性
通过以上封装方法,你可以创建一个高度可复用、灵活配置的分页组件,满足各种前端分页需求。
---
Vue3, 分页组件,组件封装,前端开发,JavaScript,Vue 组件,分页功能,前端框架,Web 开发,组件化开发,前端组件,Vue.js, 分页插件,前端技术,热门组件
---
---
资源地址:
[https://pan.quark.cn/s/50438c9ee7c0](https://pan.quark.cn/s/50438c9ee7c0)
---