典型做法:PHP 後端接收文章 ID、更新資料庫計數、回傳當前計數,前端 JavaScript 以 `document.write` 輸出。雖屬舊式寫法,但適合理解「伺服端狀態 + 客戶端渲染」基本模式。
CREATE TABLE `articles` (
`id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`title` VARCHAR(200) NOT NULL,
`content` TEXT,
`view_count` INT UNSIGNED DEFAULT 0,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
count.php<?php
// count.php
require_once __DIR__ . '/config/db.php'; // PDO 連線
// 1. 取得並驗證 ID
$id = $_GET['id'] ?? '';
if (!ctype_digit($id)) {
http_response_code(400);
exit('Invalid ID');
}
// 2. 更新計數(原子操作)
$stmt = $pdo->prepare('UPDATE articles SET view_count = view_count + 1 WHERE id = ?');
$stmt->execute([$id]);
// 3. 取得最新計數
$stmt = $pdo->prepare('SELECT view_count FROM articles WHERE id = ?');
$stmt->execute([$id]);
$count = $stmt->fetchColumn() ?? 0;
// 4. 輸出 JavaScript 程式碼供前端 document.write 使用
header('Content-Type: application/javascript; charset=UTF-8');
echo "document.write({$count} + ' 人次');";
<!-- article.php -->
<?php
// ... 取得文章內容邏輯 ...
?>
<article>
<h1>= htmlspecialchars($article['title']) ?></h1>
<div class="meta">
發布時間:= $article['created_at'] ?>
|
瀏覽:<script src="count.php?id== $article['id'] ?>"></script>
</div>
<div class="content">= $article['content'] ?></div>
</article>
| 問題 | 解決方案 |
|---|---|
| document.write 阻塞渲染、不支援非同步 | 改用 JSONP 或 Fetch API 非同步取得計數,DOM 更新 |
| 高併發下資料庫鎖爭用 | Redis 原子遞增 INCR article:views:{id},定期同步回 MySQL |
| 重複計數(刷新、爬蟲) | Session/Cookie 標記、IP+User-Agent 去重、登入用戶記錄 user_id |
| 快取衝突 | 計數器資料不快取,或使用快取標籤精準失效 |
<!-- HTML -->
<span id="view-count" data-id="= $article['id'] ?>">載入中...</span> 人次
<script>
document.addEventListener('DOMContentLoaded', () => {
const el = document.getElementById('view-count');
const id = el.dataset.id;
fetch(`/api/count.php?id=${id}`)
.then(r => r.text())
.then(text => { el.textContent = text.trim(); })
.catch(() => { el.textContent = '?'; });
});
</script>
<?php
// api/count.php (回傳純文字或 JSON)
require_once __DIR__ . '/../config/db.php';
$id = $_GET['id'] ?? '';
if (!ctype_digit($id)) { http_response_code(400); exit; }
$pdo->prepare('UPDATE articles SET view_count = view_count + 1 WHERE id = ?')->execute([$id]);
$count = $pdo->prepare('SELECT view_count FROM articles WHERE id = ?')->execute([$id])->fetchColumn();
echo (int)$count;
從 `document.write` 到 Fetch API,技術演進反映前後端分離趨勢。理解基礎原理後,實務上請採用非同步 API + 前端框架(Vue/React/Alpine)處理互動資料,後端專注提供 RESTful/GraphQL 介面。