悠悠楠杉
原生JavaScript实现高效分页功能的完整方案
原生JavaScript实现高效分页功能的完整方案
一、分页功能的核心需求分析
在Web开发中,分页功能是处理大量数据展示的必备解决方案。一个优秀的分页组件需要满足以下核心需求:
- 数据切片处理:将大数据集按指定条数分割
- 页码逻辑计算:处理首页、末页、上一页、下一页等边界情况
- DOM高效渲染:避免全量重新渲染造成的性能损耗
- 用户交互友好:提供直观的导航体验
- 响应式设计:适配不同屏幕尺寸的显示方式
二、基础分页实现方案
2.1 HTML结构搭建
html
2.2 JavaScript核心逻辑
javascript
class Pagination {
constructor(options) {
this.totalItems = options.totalItems || 0;
this.currentPage = options.currentPage || 1;
this.pageSize = options.pageSize || 10;
this.maxPages = options.maxPages || 5;
this.container = document.querySelector(options.container);
this.init();
}
init() {
this.render();
this.bindEvents();
}
get totalPages() {
return Math.ceil(this.totalItems / this.pageSize);
}
render() {
if (this.totalPages <= 1) return;
let html = '';
const { startPage, endPage } = this.calculatePaginationRange();
// 上一页按钮
html += `
<li class="page-item prev ${this.currentPage === 1 ? 'disabled' : ''}">
<a class="page-link" href="#" data-page="${this.currentPage - 1}">上一页</a>
</li>`;
// 页码按钮
for (let i = startPage; i <= endPage; i++) {
html += `
<li class="page-item ${i === this.currentPage ? 'active' : ''}">
<a class="page-link" href="#" data-page="${i}">${i}</a>
</li>`;
}
// 下一页按钮
html += `
<li class="page-item next ${this.currentPage === this.totalPages ? 'disabled' : ''}">
<a class="page-link" href="#" data-page="${this.currentPage + 1}">下一页</a>
</li>`;
this.container.innerHTML = html;
}
calculatePaginationRange() {
let startPage, endPage;
if (this.totalPages <= this.maxPages) {
startPage = 1;
endPage = this.totalPages;
} else {
const maxPagesBeforeCurrent = Math.floor(this.maxPages / 2);
const maxPagesAfterCurrent = Math.ceil(this.maxPages / 2) - 1;
if (this.currentPage <= maxPagesBeforeCurrent) {
startPage = 1;
endPage = this.maxPages;
} else if (this.currentPage + maxPagesAfterCurrent >= this.totalPages) {
startPage = this.totalPages - this.maxPages + 1;
endPage = this.totalPages;
} else {
startPage = this.currentPage - maxPagesBeforeCurrent;
endPage = this.currentPage + maxPagesAfterCurrent;
}
}
return { startPage, endPage };
}
bindEvents() {
this.container.addEventListener('click', (e) => {
e.preventDefault();
const target = e.target.closest('.page-link');
if (!target) return;
const newPage = parseInt(target.dataset.page);
if (newPage && newPage !== this.currentPage && newPage > 0 && newPage <= this.totalPages) {
this.currentPage = newPage;
this.render();
this.onPageChange();
}
});
}
onPageChange() {
// 触发自定义事件或回调
const event = new CustomEvent('pageChange', {
detail: {
currentPage: this.currentPage,
pageSize: this.pageSize
}
});
document.dispatchEvent(event);
}
}
三、高级功能扩展
3.1 动态数据加载
javascript
// 使用时
const pagination = new Pagination({
container: '#pagination',
totalItems: 150,
currentPage: 1,
pageSize: 10,
maxPages: 5
});
document.addEventListener('pageChange', (e) => {
const { currentPage, pageSize } = e.detail;
loadData(currentPage, pageSize);
});
async function loadData(page, size) {
const response = await fetch(/api/data?page=${page}&size=${size}
);
const data = await response.json();
renderData(data.items);
pagination.totalItems = data.totalCount;
pagination.render();
}
3.2 移动端适配优化
javascript
// 在render方法中添加移动端判断
render() {
const isMobile = window.innerWidth < 768;
if (isMobile) {
this.maxPages = 3;
} else {
this.maxPages = 5;
}
// 剩余渲染逻辑不变
}
3.3 添加页面跳转输入框
html
javascript
// 添加跳转逻辑
document.getElementById('goBtn').addEventListener('click', () => {
const pageNum = parseInt(document.getElementById('pageInput').value);
if (pageNum && pageNum !== this.currentPage && pageNum > 0 && pageNum <= this.totalPages) {
this.currentPage = pageNum;
this.render();
this.onPageChange();
}
});
四、性能优化建议
- 事件委托:使用单个事件监听器处理所有分页按钮点击
- 节流处理:对窗口resize事件进行节流控制
- 虚拟DOM:对大数据量考虑使用虚拟DOM技术
- 缓存机制:对已加载的页面数据进行缓存
- 请求取消:在发起新请求时取消未完成的旧请求
五、实际应用示例
javascript
// 完整示例
document.addEventListener('DOMContentLoaded', () => {
// 初始化分页
const pagination = new Pagination({
container: '#pagination',
totalItems: 0, // 初始为0,等待数据加载后更新
currentPage: 1,
pageSize: 10,
maxPages: 5
});
// 首次加载数据
loadData(1, 10);
async function loadData(page, size) {
try {
showLoading();
const response = await fetch(/api/news?page=${page}&size=${size}
);
const data = await response.json();
renderNews(data.items);
pagination.totalItems = data.totalCount;
pagination.render();
} catch (error) {
console.error('数据加载失败:', error);
} finally {
hideLoading();
}
}
function renderNews(items) {
const container = document.getElementById('news-container');
container.innerHTML = items.map(item => <div class="news-item">
<h3>${item.title}</h3>
<p>${item.summary}</p>
<time>${formatDate(item.publishTime)}</time>
</div>
).join('');
}
});
六、常见问题解决方案
页码过多显示混乱
- 实现页码截断逻辑,显示部分页码加省略号
- 使用
calculatePaginationRange
方法控制显示范围
最后一页数据不足
- 在数据请求时处理pageSize参数
- 后端应返回正确的totalCount值
并发请求导致数据错乱
- 使用AbortController取消未完成的请求
- 添加请求序列号校验
页面跳转后保持滚动位置
- 使用sessionStorage记录滚动位置
- 数据加载完成后恢复位置
通过以上方案,我们可以构建出功能完善、性能优异的分页组件,满足大多数Web应用的需求。实际开发中可根据具体业务需求进行适当调整和扩展。