如何使用Vue3开发一个Pagination公共组件

发布时间:2021-11-20 08:48:40 作者:iii
来源:亿速云 阅读:186
# 如何使用Vue3开发一个Pagination公共组件

## 目录
1. [前言](#前言)
2. [项目初始化与配置](#项目初始化与配置)
3. [基础分页组件实现](#基础分页组件实现)
4. [核心功能开发](#核心功能开发)
5. [样式设计与美化](#样式设计与美化)
6. [高级功能扩展](#高级功能扩展)
7. [组件测试](#组件测试)
8. [文档与示例](#文档与示例)
9. [总结](#总结)

## 前言

在现代Web应用中,分页(Pagination)是处理大量数据展示的必备功能。本文将详细介绍如何使用Vue3从零开始开发一个功能完善、可复用的Pagination组件,涵盖从基础实现到高级功能的完整开发流程。

### 为什么需要分页组件
- 提升大数据集下的用户体验
- 减少单次请求数据量
- 清晰的导航结构
- 适用于各种数据展示场景

### Vue3的优势
- Composition API 更好的逻辑组织
- 更小的体积和更好的性能
- TypeScript支持
- 更好的响应式系统

## 项目初始化与配置

### 1. 创建Vue3项目
```bash
npm init vue@latest vue-pagination-component
cd vue-pagination-component
npm install

2. 添加必要依赖

npm install sass classnames lodash-es

3. 项目结构规划

src/
├── components/
│   └── Pagination/
│       ├── Pagination.vue      # 主组件
│       ├── PaginationItem.vue  # 分页项子组件
│       └── style.scss          # 样式文件
├── composables/
│   └── usePagination.js        # 分页逻辑hook
├── App.vue
└── main.js

基础分页组件实现

1. 组件Props设计

// Pagination.vue
const props = defineProps({
  totalItems: {
    type: Number,
    required: true,
    default: 0
  },
  itemsPerPage: {
    type: Number,
    default: 10
  },
  currentPage: {
    type: Number,
    default: 1
  },
  maxDisplayedPages: {
    type: Number,
    default: 5
  },
  showPrevNext: {
    type: Boolean,
    default: true
  },
  showFirstLast: {
    type: Boolean,
    default: true
  }
})

2. 分页计算逻辑

// usePagination.js
import { computed } from 'vue'

export default function usePagination(props) {
  const totalPages = computed(() => 
    Math.ceil(props.totalItems / props.itemsPerPage)
  )

  const pages = computed(() => {
    const range = []
    const half = Math.floor(props.maxDisplayedPages / 2)
    let start = Math.max(props.currentPage - half, 1)
    let end = Math.min(start + props.maxDisplayedPages - 1, totalPages.value)
    
    if (end - start + 1 < props.maxDisplayedPages) {
      start = Math.max(end - props.maxDisplayedPages + 1, 1)
    }
    
    for (let i = start; i <= end; i++) {
      range.push(i)
    }
    
    return range
  })

  return { totalPages, pages }
}

3. 基础模板结构

<template>
  <nav class="pagination-container">
    <ul class="pagination">
      <li v-if="showFirstLast" class="page-item">
        <button 
          class="page-link"
          :disabled="currentPage === 1"
          @click="changePage(1)"
        >
          &laquo;
        </button>
      </li>
      
      <li 
        v-for="page in pages" 
        :key="page"
        class="page-item"
        :class="{ active: page === currentPage }"
      >
        <button class="page-link" @click="changePage(page)">
          {{ page }}
        </button>
      </li>
      
      <li v-if="showFirstLast" class="page-item">
        <button 
          class="page-link"
          :disabled="currentPage === totalPages"
          @click="changePage(totalPages)"
        >
          &raquo;
        </button>
      </li>
    </ul>
  </nav>
</template>

核心功能开发

1. 页码切换功能

const emit = defineEmits(['page-changed'])

const changePage = (page) => {
  if (page < 1 || page > totalPages.value || page === props.currentPage) return
  emit('page-changed', page)
}

2. 动态省略号处理

// 在usePagination.js中添加
const showLeftEllipsis = computed(() => pages.value[0] > 2)
const showRightEllipsis = computed(() => 
  pages.value[pages.value.length - 1] < totalPages.value - 1
)

3. 边界条件处理

// 处理itemsPerPage为0的情况
const totalPages = computed(() => {
  if (props.itemsPerPage <= 0) return 1
  return Math.ceil(props.totalItems / props.itemsPerPage)
})

// 处理currentPage越界
watch(() => props.currentPage, (newVal) => {
  if (newVal < 1) {
    emit('page-changed', 1)
  } else if (newVal > totalPages.value) {
    emit('page-changed', totalPages.value)
  }
})

样式设计与美化

1. SCSS基础样式

// style.scss
.pagination-container {
  display: flex;
  justify-content: center;
  margin: 2rem 0;
  
  .pagination {
    display: flex;
    list-style: none;
    padding: 0;
    margin: 0;
    gap: 0.5rem;
    
    .page-item {
      &.active .page-link {
        background-color: #007bff;
        color: white;
        border-color: #007bff;
      }
      
      &.disabled .page-link {
        opacity: 0.6;
        pointer-events: none;
      }
    }
    
    .page-link {
      display: flex;
      align-items: center;
      justify-content: center;
      min-width: 2.5rem;
      height: 2.5rem;
      padding: 0 0.5rem;
      border: 1px solid #dee2e6;
      border-radius: 0.25rem;
      background-color: white;
      color: #007bff;
      cursor: pointer;
      transition: all 0.2s ease;
      
      &:hover:not(.active) {
        background-color: #f8f9fa;
      }
    }
  }
}

2. 响应式设计

@media (max-width: 768px) {
  .pagination {
    flex-wrap: wrap;
    justify-content: center;
    
    .page-item {
      margin-bottom: 0.5rem;
    }
  }
}

3. 主题定制

// 添加theme prop
defineProps({
  theme: {
    type: String,
    default: 'default',
    validator: (value) => ['default', 'dark', 'minimal'].includes(value)
  }
})
// 主题样式
.pagination-container {
  &.theme-dark {
    .page-link {
      background-color: #343a40;
      color: #f8f9fa;
      border-color: #454d55;
    }
    
    .active .page-link {
      background-color: #6c757d;
    }
  }
  
  &.theme-minimal {
    .page-link {
      border: none;
      background: transparent;
    }
    
    .active .page-link {
      font-weight: bold;
      text-decoration: underline;
    }
  }
}

高级功能扩展

1. 每页显示条数选择器

// 添加props
const props = defineProps({
  // ...其他props
  showPageSizeOptions: {
    type: Boolean,
    default: false
  },
  pageSizeOptions: {
    type: Array,
    default: () => [10, 20, 50, 100]
  }
})

// 添加事件
const changePageSize = (size) => {
  emit('page-size-changed', size)
}

2. 跳转到指定页码

<div v-if="showPageJumper" class="page-jumper">
  <span>跳至</span>
  <input 
    type="number" 
    :min="1" 
    :max="totalPages"
    @keyup.enter="jumpToPage"
  >
  <span>页</span>
</div>

3. 国际化支持

// 添加locale prop
const props = defineProps({
  locale: {
    type: Object,
    default: () => ({
      first: 'First',
      last: 'Last',
      prev: 'Previous',
      next: 'Next',
      page: 'Page',
      goto: 'Go to'
    })
  }
})

4. 自定义插槽

<template #prev-text>
  <span class="custom-prev">上一页</span>
</template>

<template #page="{ page }">
  <span class="custom-page">{{ page }}</span>
</template>

组件测试

1. 单元测试

// Pagination.spec.js
import { mount } from '@vue/test-utils'
import Pagination from './Pagination.vue'

describe('Pagination', () => {
  it('renders correct number of pages', async () => {
    const wrapper = mount(Pagination, {
      props: {
        totalItems: 100,
        itemsPerPage: 10
      }
    })
    
    expect(wrapper.findAll('.page-item').length).toBe(7) // 5 pages + prev + next
  })
  
  it('emits page-changed event', async () => {
    const wrapper = mount(Pagination, {
      props: {
        totalItems: 100,
        itemsPerPage: 10,
        currentPage: 1
      }
    })
    
    await wrapper.findAll('.page-link')[2].trigger('click')
    expect(wrapper.emitted()['page-changed'][0]).toEqual([2])
  })
})

2. E2E测试

// e2e/pagination.spec.js
describe('Pagination', () => {
  it('navigates between pages', () => {
    cy.visit('/')
    cy.get('.pagination').should('exist')
    cy.get('.page-item.active').should('contain', '1')
    cy.get('.page-item').contains('2').click()
    cy.get('.page-item.active').should('contain', '2')
  })
})

文档与示例

1. 组件API文档

### Props
| 参数 | 说明 | 类型 | 默认值 |
|------|------|------|--------|
| totalItems | 总数据量 | Number | 0 |
| itemsPerPage | 每页显示条数 | Number | 10 |
| currentPage | 当前页码 | Number | 1 |
| maxDisplayedPages | 最大显示页码数 | Number | 5 |
| showPrevNext | 是否显示上一页/下一页 | Boolean | true |
| showFirstLast | 是否显示首页/末页 | Boolean | true |

### Events
| 事件名 | 说明 | 回调参数 |
|--------|------|----------|
| page-changed | 页码变化时触发 | 新页码 |
| page-size-changed | 每页条数变化时触发 | 新条数 |

2. 使用示例

<template>
  <Pagination
    :total-items="total"
    :items-per-page="pageSize"
    :current-page="currentPage"
    @page-changed="handlePageChange"
    @page-size-changed="handlePageSizeChange"
    show-page-size-options
    show-page-jumper
  />
</template>

<script setup>
import { ref } from 'vue'
import Pagination from './components/Pagination/Pagination.vue'

const total = ref(1000)
const pageSize = ref(10)
const currentPage = ref(1)

const handlePageChange = (page) => {
  currentPage.value = page
  // 这里可以发起数据请求
}

const handlePageSizeChange = (size) => {
  pageSize.value = size
  currentPage.value = 1
  // 重新请求数据
}
</script>

总结

通过本文,我们完整实现了一个功能丰富的Vue3分页组件,包含以下特性:

  1. 核心功能:基础分页、页码计算、边界处理
  2. UI设计:响应式布局、多主题支持
  3. 扩展功能:每页条数选择、页码跳转、国际化
  4. 工程化:TypeScript支持、单元测试、文档化

最佳实践建议

进一步优化方向

完整代码已托管至GitHub: vue3-pagination-component “`

推荐阅读:
  1. Element-UI之Pagination组件怎么用
  2. 怎样抽象一个Vue公共组件

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

vue3 pagination

上一篇:为什么会有代理ip软件

下一篇:JavaScript中有什么数据类型转换函数

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》