核心概念對比
| 特性 | 動態頁面 | 靜態頁面 |
| 處理流程 | 請求 → PHP 解析 → 數據庫查詢 → 生成 HTML → 響應 | 請求 → Web 服務器直接讀取文件 → 響應 |
| 服務器負載 | 高(CPU、內存、DB 連接) | 極低(僅磁盤 I/O) |
| 響應速度 | 較慢 | 極快 |
| 實時性 | 強(每次請求實時生成) | 弱(需重新生成才能更新) |
| 適用場景 | 用戶中心、購物車、實時數據 | 文章頁、產品頁、列表頁、首頁 |
模板引擎基礎
模板將業務邏輯與展示分離。簡單示例:
<!-- template.html -->
<html>
<title>{title}</title>
<body>
<h1>{title}</h1>
<div>{content}</div>
</body>
</html>
<?php
// 簡單替換式渲染
$tpl = file_get_contents('template.html');
$title = '文章標題';
$content = '文章正文內容...';
$html = str_replace(['{title}', '{content}'], [$title, $content], $tpl);
// 生成靜態文件
$file = 'article/2024/01/15/12345.html';
$dir = dirname($file);
if (!is_dir($dir)) mkdir($dir, 0755, true);
file_put_contents($file, $html);
echo "靜態頁面生成成功:$file";
?>
策略一:後台發布時主動生成(推薦高流量站點)
流程
- 管理員在後台發布/編輯內容
- 系統立即調用模板引擎生成對應 HTML 文件
- 前台鏈接直接指向靜態文件(如
/article/12345.html)
優點
- 前台零 PHP 執行,性能最優
- 可部署純靜態 Web 服務器(Nginx 靜態模塊)
缺點與解決方案
| 問題 | 解決方案 |
| 模板修改需重新生成所有頁面 | 1. 刪除整個靜態目錄,利用 404 回源機制按需重新生成 2. 頁面內嵌 <script src="/dynamic/widgets.php?id=123"></script> 載入動態片段 |
| 數萬文件導致目錄性能下降 | 按日期或 ID 分級目錄:/static/2024/01/15/12345.html 或 /static/00/00/12/345.html |
策略二:前台訪問時檢查生成(適合中小站點)
流程
- 用戶訪問
/article/12345.html
- Nginx
try_files 或 Apache ErrorDocument 404 將請求轉發給 PHP 處理器
- PHP 檢查文件是否存在:存在則重定向/直接輸出;不存在則生成並保存
- 後續訪問直接命中靜態文件
Nginx 配置示例
location /article/ {
try_files $uri $uri/ /generate.php?path=$uri;
}
generate.php 核心邏輯
<?php
$requestPath = $_GET['path']; // 如 /article/12345.html
$staticFile = __DIR__ . '/static' . $requestPath;
if (is_file($staticFile)) {
// 直接輸出文件(或重定向)
readfile($staticFile);
exit;
}
// 解析 ID、查詢數據庫、渲染模板
$id = (int) basename($requestPath, '.html');
$data = getArticleFromDb($id);
$html = renderTemplate('article.tpl', $data);
// 保存靜態文件
$dir = dirname($staticFile);
if (!is_dir($dir)) mkdir($dir, 0755, true);
file_put_contents($staticFile, $html);
echo $html;
?>
進階優化技巧
- 局部動態化:靜態頁面中通過 JS/AJAX 載入評論、點擊數、推薦列表等高頻變動模塊。
- CDN 緩存:生成靜態文件後推送至 CDN,配置長期緩存策略。
- 增量更新:僅當內容實際變更時重新生成(對比數據庫更新時間與文件修改時間)。
- 併發生成鎖:使用文件鎖或 Redis 分佈式鎖防止同一頁面並發重複生成。
完整類封裝示例
<?php
class StaticPageGenerator {
private $templateDir;
private $outputDir;
private $urlPrefix;
public function __construct(string $templateDir, string $outputDir, string $urlPrefix = '/') {
$this->templateDir = rtrim($templateDir, '/');
$this->outputDir = rtrim($outputDir, '/');
$this->urlPrefix = rtrim($urlPrefix, '/');
}
public function generate(string $templateName, array $data, string $outputPath): bool {
$tplFile = "$this->templateDir/$templateName";
if (!is_file($tplFile)) return false;
$tpl = file_get_contents($tplFile);
$html = $this->render($tpl, $data);
$fullPath = "$this->outputDir/$outputPath";
$dir = dirname($fullPath);
if (!is_dir($dir)) mkdir($dir, 0755, true);
return file_put_contents($fullPath, $html) !== false;
}
private function render(string $tpl, array $data): string {
foreach ($data as $key => $val) {
$tpl = str_replace("{${key}}", $val, $tpl);
}
return $tpl;
}
public function getUrl(string $outputPath): string {
return "$this->urlPrefix/$outputPath";
}
}
// 使用
$gen = new StaticPageGenerator(__DIR__.'/templates', __DIR__.'/static', 'https://example.com');
$gen->generate('article.tpl', ['title'=>'標題','content'=>'內容'], 'article/2024/01/15/12345.html');
echo $gen->getUrl('article/2024/01/15/12345.html');
?>
- 上一篇文章: PHP 實現標準 IP Whois 查詢:從 ARIN 根服務器遞歸獲取註冊信息
- 下一篇文章: PHP 實現無刷新登錄:利用隱藏 iframe 與 JavaScript 父頁面通信
Copyright ©
程式碼之家 All Rights Reserved