📖 项目简介

Think Card 是一套为个人站长、独立开发者、小型工作室打造的轻量级建站系统。 它同时兼顾了「内容发布」和「商品变现」两条主线:用 Copy 卡片 + 文章 + 排行 + 链接卡片组织内容, 用商品 + 订单 + 提货码完成虚拟商品的售卖与自动交付,用全站配色 + 栏目 + 底部自定义 + SEO 设置完成站点个性化。

整个系统没有使用任何后端框架、没有 Composer 依赖、没有前端打包流程, 只需要一台支持 PHP 的服务器即可运行。数据存储采用 SQLite 单文件数据库,备份只需复制一个文件。

PHP 7.4+ SQLite 单文件 零依赖 · 零构建 主题配色系统 内容 + 商城一体 全响应式

🤔 为什么要从静态 HTML 改成 PHP SSR?

Think Card 最初的架构是「静态 HTML + 前端 JS 渲染」:页面本身是空壳,浏览器加载后由 JS 请求 api.php 获取数据, 再拼接到页面中。这种架构开发简单,但在实际运行中暴露了三个越来越明显的问题。

问题一:首屏闪烁与白屏

每次刷新页面,标题、导航、内容都是异步加载的。用户会先看到一片空白,然后内容“跳”出来。 即使后来加了 localStorage 缓存方案,依然无法彻底消除闪烁——尤其是用户清空浏览器缓存或换了设备之后。

问题二:搜索引擎抓不到内容

Google 虽然能执行 JS,但百度、微信分享预览、QQ 空间等场景对 JS 渲染几乎完全忽略。 页面源代码里是空的,搜录效果很差。服务端渲染(SSR)可以解决这个问题,让爬虫直接索引到完整的 HTML 内容[reference:0]。

问题三:维护成本越来越高

每个前台页面都需要一个独立的 JS 文件来获取设置、渲染导航、渲染内容、处理分页。 applySiteSettingsrenderNavMenu 这些函数在十几个文件里反复出现。 改一处需求,要同步修改十几个地方。

最终选择:PHP 服务端渲染(SSR)

既然服务器上已经有 PHP 和 SQLite,为什么不直接在 PHP 里把数据拼进 HTML 再返回呢? 这就是 PHP SSR 的核心思想——服务器直接输出完整的 HTML 页面,浏览器只负责展示和交互[reference:1]。

这也是 SSR 最朴素的价值:更快的首次有效绘制时间、更好的 SEO、更简单的架构[reference:2]。

⚖️ 改造前后对比

数据请求流程

改造前 · 静态 HTML + JS
  • 用户请求 articles.html
  • 服务器返回空壳 HTML
  • 浏览器执行 JS,请求设置
  • 再请求栏目列表
  • 再请求文章数据
  • JS 拼接 HTML,填充页面
  • 共 4 次 HTTP 请求
改造后 · PHP SSR
  • 用户请求 articles.php
  • PHP 查数据库(设置 + 栏目 + 文章)
  • PHP 拼接完整 HTML
  • 服务器一次返回完整页面
  • 浏览器直接渲染
  • 共 1 次 HTTP 请求

性能指标对比

指标改造前改造后
首页请求数4 次(HTML + 3 API)1 次
首屏白屏时间约 300–800ms< 50ms
页面闪烁明显
百度/微信收录❌ 抓不到内容✅ 完整收录
无 JS 环境页面空白✅ 正常显示
前台 JS 文件数量13 个1 个(app.js)
缓存一致性需手动清 localStorage不需要
💡 关键变化:改造后前台只剩一个 JS 文件 app.js,负责搜索浮层、Toast 提示、复制等纯交互功能。 所有数据渲染都交给了 PHP。

🏗️ 架构设计

目录结构

├── inc/ ← 新增的公共包含目录 │ ├── config.php ← 数据库路径等配置 │ ├── bootstrap.php ← 连库、取设置、取栏目、工具函数 │ ├── head.php ← 公共部分(标题、SEO、主题色) │ ├── header.php ← 头部(Logo、导航菜单) │ └── footer.php ← 页脚、搜索浮层、引入 app.js │ ├── js/ │ └── app.js ← 唯一的前台 JS(搜索/Toast/复制) │ ├── index.php ← 首页(SSR) ├── articles.php ← 文章列表(SSR) ├── downloads.php ← 资源列表(SSR) ├── ranking.php ← 排行列表(SSR) ├── shop.php ← 商城列表(SSR) ├── all.php ← 全部内容(SSR) ├── search.php ← 搜索结果(SSR) ├── copy渲染.php ← 卡片详情(SSR) ├── download.php ← 下载详情(SSR) ├── rankingview.php ← 排行详情(SSR) ├── productpage.php ← 商品详情(SSR) ├── article.php ← 文章详情(SSR,原已存在) │ ├── pay.html ← 支付页(保持静态,纯交互) ├── pickup.html ← 提货页(保持静态,纯交互) ├── admin.html ← 后台管理(保持静态) │ ├── api.php ← 后端接口(AJAX 用) ├── data.sqlite ← SQLite 数据库 └── ...

每个 SSR 页面的结构

改造后,每个页面的结构都非常统一,只需要关注「内容区」:

<?php
require_once __DIR__ . '/inc/bootstrap.php';

// 1. 查数据
$items = $pdo->query("SELECT * FROM ...")->fetchAll();

// 2. 分页
list($page_items, $page, $total_pages) = paginate($items, 20);
?>
<!DOCTYPE html>
<html>
<head>
    <?php $page_title = '页面标题'; include 'inc/head.php'; ?>
</head>
<body>
<div class="container">
    <?php $active_nav = 'articles.html'; include 'inc/header.php'; ?>

    <!-- 内容区:PHP 直接循环输出 -->
    <?php foreach ($page_items as $it): ?>
        <div class="list-item"><?= h($it['title']) ?></div>
    <?php endforeach; ?>

    <?php render_pagination($page, $total_pages, 'articles.html'); ?>
</div>
<?php include 'inc/footer.php'; ?>
</body>
</html>

共享工具函数

inc/bootstrap.php 里集中定义了所有页面都会用到的工具函数:

函数用途
h($s)HTML 转义(所有输出到页面的数据都必须用它)
setting($key, $default)读取全站设置
sanitize_html($html)富文本净化,保留排版标签,去掉危险内容
render_breadcrumb($items)面包屑导航渲染
render_pagination($cur, $total, $base)服务端分页器
paginate($items, $size)数组分页切片
url_article($id)详情页 URL 生成(统一入口,方便以后改)

⚠️ 改造过程中踩过的坑

坑一:URL 重写导致 pay.html 404

为了让旧的 articles.html 地址继续可用,加了重写规则 RewriteRule ^([a-z0-9_-]+)\.html$ $1.php [L]。 结果 pay.html 也被映射成了 pay.php(不存在),直接 404。

修复:在通用规则之前加一条排除:

RewriteRule ^(pay|pickup|admin|edit_article|edit_ranking)\.html$ - [L]

坑二:PHP 版本低于 7.1 不支持数组解构

list($a, $b, $c) = paginate(...) 这种写法在 PHP 7.0 及以下会报语法错误。 如果你还在用旧版 PHP,把所有的 [$a, $b, $c] = ... 改成 list($a, $b, $c) = ... 即可。

坑三:pay.html 和 pickup.html 找不到 theme.js

这两个页面保持静态没做 SSR,但它们的 JS 依赖 theme.jsbreadcrumb.js。 清理旧文件时容易误删。保留这两个文件即可,它们只被这两个页面引用,不影响 SSR 页面的性能。

坑四:导航高亮匹配不上

栏目表里存的是 articles.php,但页面里写的是 articles.html,严格字符串比较匹配失败。 改成去掉 .html / .php 后缀再比较就兼容了。

🔄 SSR 之后还保留哪些 JS?

SSR 负责数据渲染,但页面的交互性还需要 JS。改造后前台只保留一个 app.js,包含三类功能:

此外,每个页面底部可能还有少量内联脚本,比如首页的轮播控制、排行详情的折叠展开、商品详情的副图切换。 这些都是几十行的纯交互代码,不需要单独抽成文件。

💡 设计原则:数据归 PHP,交互归 JS。两者不混在一起,代码更清晰,维护更简单。

✨ 功能特性总览

前台展示

模块说明
首页 index.php瀑布流展示全部内容(卡片 + 文章混排),支持分页、置顶、角标
顶部轮播两栏布局:左侧图片轮播,右侧「冷门推荐」列表
Copy 卡片详情封面 + 富文本内容介绍 + 提示词只读文本框,一键复制
下载详情支持「普通链接 / 下载链接 / 本地文件仓库」三种类型
文章列表 / 详情分类筛选、分页、置顶、角标,富文本渲染并做 HTML 净化
链接汇总链接卡片 + Copy 卡片混排,支持按分类筛选
排行列表 / 详情条目可折叠展开,标题支持自定义颜色 / 粗细 / 字号
全部内容卡片 + 文章混排的完整归档页
商城 / 商品详情商品网格(每行数量可配置)、多副图切换、库存、优惠价划线
支付页多收款二维码切换、下单后弹出提货码弹窗
提货中心输入 16 位提货码自动核销:订单码直接出结果,活动码进入多选界面
搜索全站搜索(文章 / 排行 / 商品 / 卡片),关键词高亮
面包屑全站统一面包屑导航,栏目名自动从「栏目管理」读取
主题配色8 个 CSS 变量由后台配置,PHP 直出到 <style> 中,无闪烁

后台管理

模块说明
网站信息站点标题、版本号、文章数 / Copy 卡片数 / 链接卡片数统计
内容管理四个子标签:Copy 卡片、链接卡片、文章管理、排行管理
栏目管理导航栏栏目自定义(名称、地址、排序、是否新窗口打开)
文件仓库文件分类 + 文件上传(白名单 + MIME 检测)
商品管理商品、订单、货物、提货码、数据统计
全站设置基本设置 / 首页设置 / 顶部轮播 / URL 伪静态 / 网站配色 / 底部设置
账号管理修改密码、修改用户名、多管理员管理

商城与提货系统

🚀 快速开始

环境要求

部署步骤

  1. 下载所有文件,上传到网站根目录
  2. 确保 data.sqlite 所在目录可写
  3. 访问 admin.html,使用默认账号 admin / admin123 登录
  4. 进入「全站设置」配置站点标题、副标题、Logo、SEO 信息
  5. 进入「栏目管理」添加导航栏目
  6. 开始发布内容

URL 重写规则(可选)

如果希望旧的 .html 地址继续可用,添加以下重写规则:

Apache(.htaccess)

RewriteEngine On

# 真实文件/目录直接放行
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]

# 保留 .html 的页面
RewriteRule ^(pay|pickup|admin|edit_article|edit_ranking)\.html$ - [L]

# 详情页伪静态
RewriteRule ^article-([0-9A-Za-z_\-]+)\.html$   article.php?id=$1      [L,QSA]
RewriteRule ^copy-([0-9A-Za-z_\-]+)\.html$      copy.php?id=$1         [L,QSA]
RewriteRule ^download-([0-9A-Za-z_\-]+)\.html$  download.php?id=$1     [L,QSA]
RewriteRule ^ranking-([0-9A-Za-z_\-]+)\.html$   rankingview.php?id=$1  [L,QSA]
RewriteRule ^product-([0-9A-Za-z_\-]+)\.html$   productpage.php?id=$1  [L,QSA]

# 列表页 .html → .php
RewriteRule ^([a-z0-9_-]+)\.html$               $1.php                 [L,QSA]

Nginx

location / {
    try_files $uri $uri/ @php;
}

location @php {
    rewrite ^/article-([0-9A-Za-z_-]+)\.html$   /article.php?id=$1      last;
    rewrite ^/copy-([0-9A-Za-z_-]+)\.html$      /copy.php?id=$1         last;
    rewrite ^/download-([0-9A-Za-z_-]+)\.html$  /download.php?id=$1     last;
    rewrite ^/ranking-([0-9A-Za-z_-]+)\.html$   /rankingview.php?id=$1  last;
    rewrite ^/product-([0-9A-Za-z_-]+)\.html$   /productpage.php?id=$1  last;
    rewrite ^/([a-z0-9_-]+)\.html$              /$1.php                 last;
    return 404;
}
⚠️ 注意:如果你使用 Nginx 的 try_files $uri $uri/ @php; 配置, pay.html 等静态文件会直接返回,不需要额外排除规则。上面的 Apache 配置才需要排除。

📝 总结

从静态 HTML + JS 渲染改到 PHP SSR,核心就一句话: 让服务器把数据拼进 HTML,浏览器只负责展示和交互。

改造后带来的收益:

对于那些「内容型」页面(首页、列表、详情),SSR 是更自然的选择。 而对于强交互页面(支付、提货、后台),保持静态 HTML + AJAX 反而更简单。 两者结合,才是最适合个人站长和小型项目的技术方案。