You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将基于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搜索延迟,避免用户每输入一个字符就发起请求,提升体验。

三、核心逻辑说明

  1. 后端:每次请求根据页码和关键词,先查询总记录数(用于分页组件计算总页数),再查询当前页的小范围数据,最后以JSON格式返回给前端。
  2. 前端:
    • 页面初始化时加载第一页数据。
    • 搜索时自动重置页码到1,重新请求数据。
    • 翻页时调用loadPage请求对应页码的数据,渲染完成后更新分页组件状态。
    • 保留了URL hash处理,支持通过浏览器前进/后退按钮切换页码。

改造完成后,每次翻页或搜索只会加载20条数据,彻底解决一次性加载4000+条数据的性能问题。

内容的提问来源于stack exchange,提问作者Killer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 14:52:40