首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP + AJAX 實現無刷新分頁完整實戰教程 PHP + AJAX 實現無刷新分頁完整實戰教程

     編輯:PHP 相關知識

PHP + AJAX 實現無刷新分頁完整實戰教程

無刷新分頁能顯著提升列表頁體驗,減少整頁重渲染開銷。本文從數據庫建表、原生 JS 封裝 AJAX、PHP 後端分頁邏輯、前端渲染四個維度,提供一套可直接落地的完整代碼,並對比 jQuery/Fetch 現代寫法。

第一步:數據庫準備

CREATE TABLE `tb_user` (
  `id` INT(10) NOT NULL AUTO_INCREMENT,
  `username` VARCHAR(50) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

INSERT INTO `tb_user` (`username`) VALUES
('aaa'), ('bbb'), ('ccc'), ('ddd'), ('eee'),
('fff'), ('ggg'), ('hhh'), ('用戶9'), ('用戶10');

第二步:原生 JS 封裝 AJAX(ajaxpage.js)

/**
 * 簡易 AJAX 分頁工具
 * 依賴:無(原生 XMLHttpRequest)
 * 用法:dopage('容器ID', '請求URL')
 */
var httpRequest = false;
var reobj = ''; // 目標容器 ID

function sendRequest(url) {
    httpRequest = false;
    // 兼容創建 XHR
    if (window.XMLHttpRequest) {
        httpRequest = new XMLHttpRequest();
        if (httpRequest.overrideMimeType) {
            httpRequest.overrideMimeType('text/html');
        }
    } else if (window.ActiveXObject) { // IE6-
        try { httpRequest = new ActiveXObject('Msxml2.XMLHTTP'); }
        catch (e) { try { httpRequest = new ActiveXObject('Microsoft.XMLHTTP'); } catch (e) {} }
    }
    if (!httpRequest) {
        alert('無法創建 XMLHttpRequest 對象');
        return false;
    }
    httpRequest.onreadystatechange = processRequest;
    httpRequest.open('GET', url, true);
    httpRequest.send(null);
}

function processRequest() {
    if (httpRequest.readyState === 4) {
        if (httpRequest.status === 200) {
            document.getElementById(reobj).innerHTML = httpRequest.responseText;
        } else {
            alert('請求失敗:' + httpRequest.status);
        }
    }
}

function dopage(obj, url) {
    document.getElementById(obj).innerHTML = '正在加載...';
    reobj = obj;
    sendRequest(url);
}

第三步:PHP 後端邏輯(index.php)

<?php
// 配置
$pageSize = 3;
$host = 'localhost'; $user = 'root'; $pass = ''; $dbname = 'test';
$mysqli = new mysqli($host, $user, $pass, $dbname);
if ($mysqli->connect_errno) die('DB連接失敗: ' . $mysqli->connect_error);
$mysqli->set_charset('utf8mb4');

// 參數
$page = isset($_GET['page']) ? max(1, intval($_GET['page'])) : 1;
$url = 'index.php'; // 當前頁面 URL

// 總記錄數
$totalRes = $mysqli->query("SELECT COUNT(*) AS cnt FROM tb_user");
$total = $totalRes->fetch_assoc()['cnt'];
$totalRes->free();

$pageCount = ceil($total / $pageSize);
$page = min($page, $pageCount);
$offset = ($page - 1) * $pageSize;

// 查詢數據
$dataRes = $mysqli->query("SELECT * FROM tb_user ORDER BY id DESC LIMIT $offset, $pageSize");
$rows = $dataRes->fetch_all(MYSQLI_ASSOC);
$dataRes->free();
$mysqli->close();

// 生成分頁導航 HTML
$pagenav = '';
$pagenav .= "顯示第 " . ($total ? $offset+1 : 0) . "-" . min($offset+$pageSize, $total) . " 條,共 $total 條,第 $page 頁 / 共 $pageCount 頁 ";

$prev = $page > 1 ? $page - 1 : 0;
$next = $page < $pageCount ? $page + 1 : 0;

$pagenav .= $prev ? "上一頁 " : "上一頁 ";
$pagenav .= $next ? "下一頁 " : "下一頁 ";
$pagenav .= $pageCount ? "尾頁" : "";
?>



    
    AJAX 分頁演示
    
    


    
ID用戶名

第四步:現代化重構(Fetch + JSON API)

後端 API (api.php):返回 JSON,分離關注點。

<?php
header('Content-Type: application/json; charset=utf-8');
$mysqli = new mysqli('localhost', 'root', '', 'test');
$page = max(1, intval($_GET['page'] ?? 1));
$pageSize = 3;
$offset = ($page - 1) * $pageSize;

$total = $mysqli->query("SELECT COUNT(*) c FROM tb_user")->fetch_row()[0];
$res = $mysqli->query("SELECT id, username FROM tb_user ORDER BY id DESC LIMIT $offset, $pageSize");
$data = $res->fetch_all(MYSQLI_ASSOC);

echo json_encode([
    'page' => $page,
    'pageSize' => $pageSize,
    'total' => $total,
    'pageCount' => ceil($total / $pageSize),
    'data' => $data
], JSON_UNESCAPED_UNICODE);

前端渲染 (app.js):

async function loadPage(page) {
    const container = document.getElementById('result');
    container.innerHTML = '加載中...';
    try {
        const resp = await fetch(`api.php?page=${page}`);
        const json = await resp.json();
        renderTable(json.data);
        renderPager(json);
    } catch (e) {
        container.innerHTML = '加載失敗: ' + e.message;
    }
}

function renderTable(rows) {
    let html = '';
    rows.forEach(r => html += ``);
    html += '
ID用戶名
${r.id}${escapeHtml(r.username)}
'; document.getElementById('tableWrap').innerHTML = html; } function renderPager({page, pageCount, total}) { let html = `顯示第 ${(page-1)*3+1}-${Math.min(page*3, total)} 條,共 ${total} 條,第 ${page}/${pageCount} 頁 `; if (page > 1) html += ` `; if (page < pageCount) html += ` `; document.getElementById('pagerWrap').innerHTML = html; } function escapeHtml(str) { return str.replace(/[&<>"']/g, c => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c])); } // 初始化 loadPage(1);

常見問題與優化

總結

從原生 XMLHttpRequest 到 Fetch API,核心邏輯不變:前端請求 → 後端分頁查詢 → 返回片段/JSON → 前端局部更新。生產建議:

  1. 採用 JSON API + 前端模板渲染 模式,前後端分離更清晰。
  2. 引入 axios 或 swr/react-query 管理請求狀態、緩存、去抖。
  3. 分頁參數統一規範(page/per_page 或 offset/limit),配合 OpenAPI 文檔。
  4. 大數據量場景改用「游標分頁」或「無限滾動」,避免 OFFSET 深分頁性能劣化。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
問題解決方案
全選/反選失效動態插入的 checkbox 需事件委託:document.getElementById('result').addEventListener('change', e => { if(e.target.matches('.chk')) ... }),或渲染後重新綁定。
瀏覽器前進/後退不恢復分頁狀態使用 History API:history.pushState({page}, '', `?page=${page}`),監聽 popstate 事件恢復。
併發請求導致頁碼亂序加鎖標誌位 let loading = false,請求前檢查,完成後釋放。
SEO 不友好服務端渲染首屏(如上方 PHP 直出),AJAX 只負責翻頁;或使用 Nuxt/Next.js 同構方案。