如何将基于Div的客户端分页改造为服务端分页?
改造为服务端分页的实现方案
要把现有的客户端分页改成服务端分页,核心是让每次翻页、搜索都通过异步请求从Oracle数据库加载对应页的20条数据,而非一次性加载全量数据再在客户端做隐藏/显示。下面分后端(PHP+Oracle)和前端(JS+simplePagination.js)两部分逐步实现:
一、后端改造(PHP)
1. 创建分页数据接口(比如fetch_data.php)
这个文件负责接收前端的分页、搜索参数,查询Oracle数据库后返回分页数据和总记录数:
<?php // 1. 连接Oracle数据库(替换为你的实际连接信息) $conn = oci_connect('username', 'password', 'localhost/XE'); if (!$conn) { $e = oci_error(); trigger_error(htmlentities($e['message'], ENT_QUOTES), E_USER_ERROR); } // 2. 获取前端传参 $page = isset($_GET['page']) ? intval($_GET['page']) : 1; $per_page = 20; // 每页固定20条,和前端保持一致 $keyword = isset($_GET['keyword']) ? trim($_GET['keyword']) : ''; // 3. 计算分页范围 $start_row = ($page - 1) * $per_page + 1; $end_row = $page * $per_page; // 4. 查询总记录数(用于分页组件计算总页数) $count_sql = "SELECT COUNT(*) AS total FROM your_table_name"; if (!empty($keyword)) { $count_sql .= " WHERE your_search_column LIKE :keyword"; } $count_stmt = oci_parse($conn, $count_sql); if (!empty($keyword)) { $keyword_param = '%' . $keyword . '%'; oci_bind_by_name($count_stmt, ':keyword', $keyword_param); } oci_execute($count_stmt); $total_rows = oci_fetch_assoc($count_stmt)['TOTAL']; // 5. 查询当前页数据(适配Oracle 11g的分页写法) $data_sql = " SELECT * FROM ( SELECT t.*, ROWNUM AS rn FROM your_table_name t WHERE 1=1 " . (!empty($keyword) ? " AND t.your_search_column LIKE :keyword" : "") . " ORDER BY your_sort_column -- 必须加排序,保证分页数据顺序稳定 ) WHERE rn BETWEEN :start_row AND :end_row "; $data_stmt = oci_parse($conn, $data_sql); if (!empty($keyword)) { oci_bind_by_name($data_stmt, ':keyword', $keyword_param); } oci_bind_by_name($data_stmt, ':start_row', $start_row); oci_bind_by_name($data_stmt, ':end_row', $end_row); oci_execute($data_stmt); // 6. 整理返回数据 $rows = []; while ($row = oci_fetch_assoc($data_stmt)) { $rows[] = $row; } // 7. 返回JSON格式结果 header('Content-Type: application/json'); echo json_encode([ 'total' => $total_rows, 'data' => $rows, 'page' => $page, 'per_page' => $per_page ]); // 清理资源 oci_free_statement($count_stmt); oci_free_statement($data_stmt); oci_close($conn); ?>
后端注意事项:
- 替换
your_table_name、your_search_column、your_sort_column为你的实际表名、搜索字段和排序字段。 - 如果使用Oracle 12c+,可以用更简洁的
FETCH NEXT :per_page ROWS ONLY OFFSET :offset ROWS语法替代ROWNUM嵌套写法。
二、前端改造(JS+HTML)
1. 调整HTML结构
确保有一个专门的容器用来动态渲染每页的Div:
<!-- 搜索框保持原有结构 --> <input name="keyword" type="text" class="form-control" placeholder="Pesquisar" aria-describedby="sizing-addon1"> <!-- 新增:动态内容容器,用于渲染每页的Div --> <div id="content-container"></div> <!-- 分页组件容器 --> <div id="pagination"></div>
2. 重写JS逻辑
替换原来的客户端分页代码,改为异步请求后端接口的逻辑:
$(function() { const keywordInput = document.querySelector("input[name='keyword']"); const contentContainer = $('#content-container'); const paginationContainer = $('#pagination'); const perPage = 20; // 和后端保持一致 // 渲染单条数据的Div(替换为你原来的Div构建逻辑) function renderData(rows) { contentContainer.empty(); rows.forEach(row => { // 这里根据你的数据结构生成对应Div,示例用占位字段 const panelDiv = $(` <div class="content panel"> <h4>${row['YOUR_COLUMN1']}</h4> <p>${row['YOUR_COLUMN2']}</p> </div> `); contentContainer.append(panelDiv); }); } // 加载指定页数据的核心函数 function loadPage(pageNum = 1) { const keyword = keywordInput.value.trim(); $.ajax({ url: 'fetch_data.php', type: 'GET', data: { page: pageNum, keyword: keyword }, dataType: 'json', success: function(response) { // 渲染当前页数据 renderData(response.data); // 更新分页组件(先销毁旧实例,再初始化新的) paginationContainer.pagination('destroy'); paginationContainer.pagination({ items: response.total, itemsOnPage: perPage, currentPage: pageNum, cssStyle: 'light-theme', onPageClick: function(pageNumber) { loadPage(pageNumber); } }); }, error: function() { alert('加载数据失败,请稍后重试'); } }); } // 搜索框输入监听(加延迟避免频繁请求) let searchTimeout; keywordInput.addEventListener('input', function() { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { loadPage(1); // 搜索后自动回到第一页 }, 300); }); // 页面初始化加载第一页 loadPage(1); // 处理URL hash(支持前进后退切换页码) function checkFragment() { const hash = window.location.hash || '#page-1'; const match = hash.match(/^#page-(\d+)$/); if (match) { const pageNum = parseInt(match[1]); loadPage(pageNum); } } $(window).bind('popstate', checkFragment); checkFragment(); });
前端注意事项:
- 把
renderData函数里的Div结构替换为你原来在PHP游标中构建的内容,用row['字段名']填充真实数据。 - 确保后端接口
fetch_data.php的路径正确。 - 加入300ms搜索延迟,避免用户每输入一个字符就发起请求,提升体验。
三、核心逻辑说明
- 后端:每次请求根据页码和关键词,先查询总记录数(用于分页组件计算总页数),再查询当前页的小范围数据,最后以JSON格式返回给前端。
- 前端:
- 页面初始化时加载第一页数据。
- 搜索时自动重置页码到1,重新请求数据。
- 翻页时调用
loadPage请求对应页码的数据,渲染完成后更新分页组件状态。 - 保留了URL hash处理,支持通过浏览器前进/后退按钮切换页码。
改造完成后,每次翻页或搜索只会加载20条数据,彻底解决一次性加载4000+条数据的性能问题。
内容的提问来源于stack exchange,提问作者Killer
相关产品推荐
相关产品推荐

