mdView 文档
欢迎使用 mdView 文档系统
👁8 次阅读·📖约 6 分钟

title: 静态化部署

description: 将 mdView 动态文档站编译为纯静态 HTML 文件,实现零 PHP 依赖的极速访问体验

keywords: 静态化,部署,编译,CDN,Nginx,GitHub Pages

概述

mdView 内置静态化构建器,可将所有文档预编译为纯静态 HTML 文件。编译后的站点无需 PHP 环境,可直接部署到 Nginx、CDN 甚至 GitHub Pages。

核心思路:只静态化"公开阅读页面",管理后台、登录、分享等动态功能保持不变。

与动态站对比

维度动态站(PHP)静态站 首次响应50~200ms(PHP + 数据库)< 10ms(纯文件) 并发能力受 PHP 吞吐限制几乎无上限 SEO正常更优(首字节更快) 服务器要求PHP 7.4+任意 Web 服务器 权限/门禁支持静态站全量公开 文档更新实时(读文件系统)需重建 搜索服务端 SQL 查询客户端 JSON 匹配

一键构建

# 默认输出到 static/ 目录
php build-static.php

# 指定输出目录和自定义域名
php build-static.php /var/www/site https://docs.example.com

构建日志示例:

╔══════════════════════════════════════╗
║     mdView 静态网站构建器           ║
╚══════════════════════════════════════╝

输出目录: static/
Base URL: http://localhost

  搜索索引: 20 条
═══════════════════════════════════════
  构建完成!
  文档页: 20 个
═══════════════════════════════════════

输出目录结构

static/
├── index.html                  ← 首页(自动跳转到 intro)
├── intro/index.html            ← 每个文档一个独立目录
├── quickstart/index.html
├── 开发/
│   ├── 01-系统/index.html
│   ├── 01.开发相关/
│   │   ├── 01.开发环境/index.html
│   │   └── 02.前端编译/index.html
│   └── ...
├── 系统指南/
│   ├── api/index.html
│   ├── env/index.html
│   └── ...
├── search.json                 ← 客户端搜索索引
├── sitemap.xml                 ← 搜索引擎站点地图
├── robots.txt
├── assets/                     ← 静态资源
└── docs/                       ← 图片等文档附件

部署方式

方案 A:Nginx

server {
    listen 80;
    server_name docs.example.com;
    root /var/www/mdView/static;
    index index.html;

    # /intro → /intro/index.html
    location / {
        try_files $uri $uri/index.html =404;
    }

    # 静态资源缓存
    location /assets/ {
        expires 30d;
    }
}

方案 B:Apache

<VirtualHost *:80>
    ServerName docs.example.com
    DocumentRoot /var/www/mdView/static

    <Directory /var/www/mdView/static>
        Options -Indexes +FollowSymLinks
        AllowOverride All
    </Directory>
</VirtualHost>

同时确保 static/.htaccess

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteRule ^(.*)$ $1.html [L]

方案 C:GitHub Pages / Vercel

直接将 static/ 目录推送到仓库,设置 Pages 源为根目录即可。

方案 D:动态站 + 静态站共存

将 Nginx 配置为:

  • /admin/login/s/ 等路由代理到 PHP 动态站
  • 其余路径走静态文件
  • 方案 E:动态站内置静态回退(推荐)

    适用场景:只需部署一套 PHP 服务,文档页面自动享受静态文件的速度,无需额外 Web 服务器配置。

    核心思路:index.php 在处理文档请求时,先检查 static/ 目录下是否有对应的预编译 HTML 文件;存在则直接输出(零数据库开销),不存在则回退到动态渲染。

    浏览器请求 /intro
           │
           ▼
       index.php(路由分发)
           │
           ├─ /admin /search /login ...  → 直接进入对应模块(不检查静态文件)
           │
           └─ 其他(文档路径)
                  │
                  ▼
             tryServeStatic()
                  │
             static/intro/index.html 存在?
                  │                │
                 YES              NO
                  │                │
                  ▼                ▼
             readfile()       require vitep.php
             直接输出           动态渲染(PHP + 数据库)
             X-Served-By: static
    

    PHP 层实现index.phptryServeStatic() 函数):

    function tryServeStatic(string $path): bool
    {
        // 首页 / → static/index.html
        if ($path === '') {
            $staticFile = __DIR__ . '/static/index.html';
            if (file_exists($staticFile)) {
                header('Content-Type: text/html; charset=utf-8');
                header('X-Served-By: static');
                readfile($staticFile);
                return true;
            }
            return false;
        }
        // 文档路径 → static/{path}/index.html
        $staticFile = __DIR__ . '/static/' . $path . '/index.html';
        if (file_exists($staticFile)) {
            header('Content-Type: text/html; charset=utf-8');
            header('X-Served-By: static');
            readfile($staticFile);
            return true;
        }
        return false;
    }
    

    注意:adminsearchloginlogoutupgrades/vitep 等动态路由位于 index.phpelse 分支之前,在到达 tryServeStatic() 前已被提前拦截,无需额外排除逻辑。

    Web 服务器层优化(可选,性能更优):

    .htaccess 或 Nginx 中增加静态文件检查规则,将命中率最高的请求在 Web 服务器层面直接返回,连 PHP 进程都不需要启动。与方案 E 的 PHP 层回退可以叠加使用(Web 服务器先拦,未命中再由 PHP 兜底)。

    # Apache .htaccess — 插在原有 RewriteRule 之前
    RewriteCond %{REQUEST_URI} !^/admin
    RewriteCond %{REQUEST_URI} !^/search
    # ... 其余动态路由排除
    RewriteCond %{DOCUMENT_ROOT}/static/%{REQUEST_URI}/index.html -f
    RewriteRule ^(.*)$ /static/$1/index.html [L,END]
    

    # Nginx — 插入 location / 块
    location / {
        if ($uri ~ ^/(admin|search|api|vitep|s/|login|logout|upgrade)) {
            rewrite ^ /index.php last;
        }
        try_files /static/$uri/index.html /index.php?$args;
    }
    

    路由分类速查

    路由模式走静态?说明 `/`✅`static/index.html` `/intro`、`/quickstart`、`/开发/...` 等文档路径✅`static/{path}/index.html` `/admin/*`❌管理后台,需登录态 `/search`❌服务端搜索 API `/api/doc/*`❌RESTful 接口 `/s/{token}`❌需验证分享令牌 `/login`、`/logout`❌认证流程 `/vitep/*`❌内部路由 `/sitemap.xml`、`/robots.txt`❌实时生成

    诊断标记

    静态文件返回时会携带响应头 X-Served-By: static,可在浏览器开发者工具 Network 面板中查看,便于确认当前命中的是静态还是动态渲染。

    静态页面包含的功能

    每个生成的 HTML 页面完整包含:

    功能实现方式 侧边栏导航构建时根据文档树生成,当前页高亮 面包屑构建时计算完整路径链 本页目录(TOC)构建时从 Markdown 标题提取 上/下篇导航构建时计算前一篇/后一篇链接 顶部菜单从数据库菜单表读取并内联 广告位从数据库广告表读取并内联 推荐文档基于浏览量排序的热点文章 深色/浅色主题纯客户端 JS,localStorage 记忆 字号切换纯客户端 JS 移动端适配响应式布局,侧边栏折叠 搜索读取 search.json,客户端匹配过滤 SEO Metatitle、description、keywords、OG、Twitter Card、JSON-LD

    不包含的功能

    以下动态功能在静态站中不可用,请通过动态站访问:

  • 用户登录 / 退出
  • 阅读配额检查
  • 权限门禁(静态站全量公开)
  • 文档分享页(/s/{token}
  • 管理后台
  • 阅读量统计
  • 定时重建

    文档更新后需手动重建。可通过 cron / 计划任务自动执行:

    # Linux cron(每小时)
    0 * * * * cd /path/to/mdView && php build-static.php static https://docs.example.com
    
    # Windows 计划任务
    schtasks /create /tn "mdView-Static" /tr "php E:\mdView\build-static.php" /sc hourly
    

    技术架构

    构建流程

    ┌──────────────┐     ┌─────────────────────────────────┐
    │  CLI 入口     │────▶│  StaticBuilder                   │
    │  build-static │     │                                 │
    └──────────────┘     │  ① 读取数据库文档树              │
                         │  ② 遍历每篇 .md 文件              │
                         │  ③ MarkdownParser 解析为 HTML     │
                         │  ④ 内联 CSS(assets/vitep.css)   │
                         │  ⑤ 内联 JS(vitep.php + vitep.js  │
                         │     + 客户端搜索模块)            │
                         │  ⑥ 组装完整 HTML 页面             │
                         │  ⑦ 写入 static/ 目录              │
                         │  ⑧ 生成 search.json 搜索索引      │
                         │  ⑨ 复制 assets/ 资源文件          │
                         └──────────────┬────────────────────┘
                                        │
                                        ▼
                                static/
                                ├── index.html
                                ├── intro/index.html
                                ├── search.json
                                ├── assets/
                                └── ...
    

    请求服务流程(混合部署模式)

                            用户浏览器
                                │
                        GET /intro HTTP
                                │
                                ▼
                         ┌─────────────┐
                         │  Web 服务器  │(可选层:.htaccess / Nginx)
                         │  静态检查    │
                         └──────┬──────┘
                                │
                   static/intro/index.html 存在?
                       │               │
                      YES             NO
                       │               │
                       ▼               ▼
                ┌──────────┐    ┌──────────┐
                │ 直接返回  │    │index.php │
                │ 静态文件  │    │ 动态渲染  │
                │ < 1ms    │    └────┬─────┘
                └──────────┘         │
                               tryServeStatic()
                                     │
                        static/intro/index.html 存在?
                            │               │
                           YES             NO
                            │               │
                            ▼               ▼
                     ┌──────────┐   ┌──────────────┐
                     │readfile()│   │vitep.php     │
                     │直接输出   │   │数据库 + 渲染  │
                     │10~30ms   │   │50~200ms      │
                     └──────────┘   └──────────────┘