本文由 FlintHub 开发文档归档发布。原始文档日期见标题前缀,来源:20260806备份/重构总结_20260806.md。
SoSite — Alpine.js + htmx 重构完整总结(三合一)
整理日期:2026-08-06
执行:AtomCode(deepseek-v4-flash)
本文档整合自以下三份文档,覆盖 SoSite 前端交互重构的计划 → 第一轮实施(07-20)→ 第二轮补全(08-06)全过程:
1. 《htmx+Alpine.js 重构计划(修正版).md》——改造方案与核心原则
2. 《会话总结-Alpine+htmx改造.md》——2026-07-20 第一轮实施记录
3. 《重构总结_20260806.md》——2026-08-06 剩余工作补全记录(本三合一文档在 08-06 版基础上扩写)
目录
- 总体背景与目标
- 第一部分:重构计划(修正版)要点
- 第二部分:第一轮改造总结(2026-07-20)
- 第三部分:第二轮补全总结(2026-08-06)
- 综合踩坑记录(三轮合并去重)
- 综合改造对照表
- 全站功能验证(两轮汇总)
- 备份与回滚
- 核心原则(下次改造必读)
- 附录:Alpine.js / htmx 常用指令速查
- 后续建议
1. 总体背景与目标
SoSite 是单人维护的轻量级 PHP 社区,存在大量零散 JS:内联 onclick/onsubmit/onchange
事件、多个独立 JS 文件(nav.js/vote.js/插件 script.js)、手写 XMLHttpRequest/fetch 样板。
改造总目标:引入 Alpine.js + htmx,消除零散 JS、统一交互模式与 AJAX 模式,
保持原有 CSS 体系不变,不引入任何构建工具链。
引入的库:
| 库 | 版本 | 大小 | 用途 |
|---|---|---|---|
assets/lib/alpine.min.js | v3.15.x | ~10KB gzip | 所有交互行为 |
assets/lib/htmx.min.js | v2.x | ~14KB gzip | 所有 AJAX 请求 |
2. 第一部分:重构计划(修正版)要点
2.1 血的教训(计划阶段定下的红线)
- 不要引入 Tailwind CSS:SoSite 已有自洽的
mn-*类名体系和 CSS 文件拆分,
Tailwind 收益为零、代价实实在在(两套体系混用、构建工具链)
- 不要做"机械替换":把
mn-flex-center改成flex items-center没有解决任何问题 - 改造前先想清楚收益,每次改动问三个问题:
- 用户能感知到吗?→ 不能就不改
- 维护成本降低了吗?→ 没降低就不改
- 后续扩展更方便吗?→ 没变好就不改
2.2 改造范围(只改什么 / 不改什么)
值得改的:
onclick="confirm(...)"→ Alpinex-on:submit.prevent(~30 处)onclick="toggleNav()"→ Alpinex-on:click(4 处,消除 nav.js)onclick="showTab()"→ Alpinex-on:click+x-show(3 处)onclick="toggleVote()"→ htmxhx-post(8 处,消除 vote.js)onchange="toggleAll()"→ Alpinex-on:change(4 处)- 骰子/猜题 XMLHttpRequest → htmx
hx-post(消除 AJAX 样板) - 签到表单提交 → htmx
hx-post(消除页面刷新) - 模态框显隐 → Alpine
x-show
不改的:
- ❌ CSS 体系(
mn-/c-/fs-*类名、8 个旧 CSS 文件) - ❌ 动态 PHP 颜色(
style="color:<?php echo $thread['color']; ?>"等) - ❌ CKEditor(
editor.js/editor.css不修改) - ❌ FontAwesome 图标系统(
$this->icon()不修改) - ❌ 不引入 Tailwind / 不构建 CSS 工具链
2.3 四阶段计划
| 阶段 | 内容 |
|---|---|
| 第一阶段 | 引入基础环境:复制静态文件到 assets/lib/,两个 layout 引入 Alpine + htmx,CSP 加 'unsafe-eval' |
| 第二阶段 | Alpine.js 替换交互:confirm 弹窗、模态框、导航切换、Tab 切换、全选/取消 |
| 第三阶段 | htmx 替换 AJAX:骰子、猜题对决、每日签到、投票 |
| 第四阶段 | 收尾清理:删除废弃 JS 文件(nav.js/vote.js/插件 script.js)、删除 ziyuan 目录 |
3. 第二部分:第一轮改造总结(2026-07-20)
3.1 改造目标与已遵守原则
引入 Alpine.js 和 htmx,替换原有零散 JS 交互和 AJAX 请求。不引入 Tailwind CSS,不动现有 CSS 体系。
已遵守的核心原则:
- ✅ 不改 CSS 类名(
mn-、c-、fs-*保留) - ✅ 不改内联样式(动态 PHP 颜色保留)
- ✅ 不改编辑器(CKEditor 独立)
- ✅ 不改图标系统(FontAwesome 原样)
- ✅ 未引入 Tailwind CSS
- ✅ 未引入任何构建工具(npm/Webpack/Vite)
- ✅ 每次改完做 PHP 语法检查
3.2 第一轮文件变更清单(~30 个文件)
新增文件:
| 文件 | 说明 |
|---|---|
assets/lib/alpine.min.js | Alpine.js v3.x 运行时(约 46KB) |
assets/lib/htmx.min.js | htmx v2.x 运行时(约 51KB) |
修改文件:
| 类别 | 文件 | 改造内容 |
|---|---|---|
| 布局(2) | app/Views/layouts/main.php、assets/themes/modern/templates/layouts/main.php | 引入 Alpine + htmx,导航切换 Alpine 化,修复下拉菜单 |
| 核心(1) | app/Core/Controller.php | CSP 添加 'unsafe-eval'(Alpine 必需) |
| Admin(~15) | app/Views/admin/blog_categories.php、categories.php、database.php、groups.php、levels.php、maintenance.php、plugins.php、search_settings.php、tags.php、themes.php、threads.php、users.php、blog_manage.php | confirm 弹窗 Alpine 化 + 全选/取消 Alpine 化 |
| Admin | app/Controllers/Admin/ThreadController.php | 添加 needsEditor => true |
| Message(3) | app/Views/message/inbox.php、sent.php、view.php | confirm 删除 Alpine 化 |
| Profile(1) | assets/themes/modern/templates/profile/index.php | Tab 切换 Alpine 化 |
| Thread(2) | app/Views/thread/show.php、assets/themes/modern/templates/thread/show.php | confirm 删除 + 投票 htmx 化 |
| API(1) | app/Controllers/ApiController.php | 添加 htmx HTML 片段响应 |
| Vote(1) | app/Helpers/Vote.php | renderButtons 改用 htmx 属性 |
| 骰子(2) | app/Views/plugins/dice/index.php、plugins/dice/DiceController.php | 骰子 AJAX htmx 化 |
| 猜题(3) | app/Views/plugins/quiz_duel/create.php、detail.php、plugins/quiz_duel/QuizDuelController.php | 创建挑战/应战 htmx 化 |
| 签到(3) | app/Views/plugins/daily_checkin/index.php + Modern 版、plugins/daily_checkin/CheckinController.php | 签到 htmx 化 |
| 插件后台模态框(6) | announcements/content_review/friend_links 的 admin.php + Modern 版 | 模态框 Alpine 化 |
| 插件后台 confirm(6) | content_review/admin.php、notifications.php、friend_links/admin.php、qa/admin/index.php、tags.php | confirm Alpine 化 |
删除文件:
| 文件 | 原因 |
|---|---|
assets/js/nav.js | 导航切换已用 Alpine 替代 |
assets/js/vote.js | 投票已用 htmx 替代 |
plugins/dice/assets/script.js | 骰子 AJAX 已用 htmx 替代 |
plugins/quiz_duel/assets/script.js | 猜题 AJAX 已用 htmx 替代 |
assets/lib/s-state.js、assets/lib/s-fetch.js | 包装文件,已废弃 |
3.3 第一轮踩坑记录(6 条)
- CSP 安全策略:Alpine 完全不工作、控制台无报错 → CSP 头缺
'unsafe-eval'→ Controller.php 的 script-src 中加入 - x-show 与 CSS display:none 冲突:元素一直不显示 → CSS 权重高于 Alpine 内联样式 → 改用
:class在父容器绑定类名 - $refs 作用域问题:
$refs.navLinks为 undefined →$refs只能在x-data作用域内访问 → 把x-data移到共同父元素,用:class替代 - 投票图标不显示:ApiController 返回 HTML 用 CSS 类方式(
fa fa-thumbs-o-up),项目用 Unicode 字符实体()→ 统一为 Unicode 字符实体 - 个人中心 Tab 全部显示:
x-data在 Tab 导航上、x-show内容在同级不在作用域 → 把x-data移到共同父容器 - 批量替换导致
$el.丢失:confirm 弹窗中$el.submit()变成.submit()→ 正则批量替换时$el被当作捕获组引用 → 逐个修复受影响文件(⚠️ 本轮发现 QA admin 与 friend_links admin 漏网,见第三部分)
3.4 第一轮遗留的剩余工作
- 测试全站功能
- Modern 主题论坛
category.php检查 - 默认模板
profile/index.php确认 - 其他插件前台(QA 等)未动
- 删除 ziyuan 目录
4. 第三部分:第二轮补全总结(2026-08-06)
4.1 本轮完成内容(7 项任务全部完成)
| # | 任务 | 结果 |
|---|---|---|
| 1 | 备份要改动的文件 | ✅ _backup_20260806/ |
| 2 | 检查 Modern 主题论坛 category.php | ✅ 无需改造(纯展示页) |
| 3 | 检查并改造默认模板 profile/index.php | ✅ Tab 切换 Alpine 化 |
| 4 | 检查并改造 QA 问答插件前台页面 | ✅ 前台 htmx 化 + 后台遗留修复 |
| 5 | 检查其他插件前台零散 JS | ✅ content_review / post_favorite / friend_links / announcements / red_packet |
| 6 | 全站功能测试(语法 + 页面访问) | ✅ 全部通过 |
| 7 | 确认资源复制后删除 ziyuan 目录 | ✅ 已删除(有备份) |
4.2 默认模板 profile/index.php — Tab 切换 Alpine 化
改造前:页面底部 DOMContentLoaded 内联 <script>,通过 classList / style.display 手动切换 4 个 Tab。
改造后:
基本资料
...
- 菜单项:
x-on:click.prevent+:class动态高亮;内容区:x-show - 内联
<script>整段删除;x-init保留锚点直达(外部链接#info直达对应 Tab)
4.3 QA 问答插件 — 前台 htmx 化(重点)
改造前:3 类零散 JS —— qaVote()(XHR 投票)、qaAccept()(XHR 采纳 + confirm)、
qaAnswerForm/qaAskForm(fetch 提交回答/提问)。
改造后:
| 功能 | 改造前 | 改造后 |
|---|---|---|
| 问题/回答投票 | onclick="qaVote('q', id, 1)" + XHR | hx-post="/qa/vote" + hx-vals='{"type":"q","id":ID,"vote":1,"csrf":"..."}' + hx-target="#vote_q_ID" + hx-swap="outerHTML" |
| 采纳回答 | onclick="qaAccept(id)" + XHR + confirm | hx-post="/qa/accept/ID" + hx-vals='{"csrf":"..."}' + hx-confirm |
| 提交回答 | fetch + JSON | 表单 hx-post="/qa/question/ID/answer" + HX-Redirect 头 |
| 提问/编辑 | fetch + JSON | 表单 hx-post="/qa/ask" / /qa/edit/ID + HX-Redirect 头 |
QaController 新增 htmx 分支(vote/accept/answer/save/edit 五个方法):
vote():htmx 请求返回「新计数 span + script」,同时更新按钮voted高亮与内联颜色accept():成功后返回HX-Redirect头,整页刷新(采纳改变排序与标记)answer()/save()/edit():成功返回HX-Redirect头;校验失败返回
<div class="qa-form-error">错误信息</div> 到表单错误容器
- 所有错误分支均补
HTTP_HX_REQUEST判断,防止 htmx 场景返回 JSON 文本
默认模板 + Modern 主题共 4 个前台视图同步改造,内联 <script> 整段删除。
4.4 其他插件前台与后台遗留修复
content_review 通知页(前台):
- 默认模板:删除残留死代码
toggleNoti()(已被 Alpinex-on:change全选取代) - Modern 模板:
onsubmit="return confirm(...)"→ Alpinex-on:submit.prevent;onchange="toggleNoti(this)"→ Alpine 全选;删除死代码
post_favorite 收藏页(前台):
- 「取消收藏」表单(默认 + Modern):改为
hx-post="/favorite/toggle"+hx-target="closest .fav-item / .mn-row"+hx-swap="outerHTML"(取消后整行消失) FavoriteController::toggle()增加 htmx 分支:成功返回空片段assets/script.js精简:删除已被 htmx 取代的fav-del-formfetch;保留帖子页收藏按钮注入(依赖动态 DOM 注入,暂不 htmx 化)
后台 admin 遗留修复(上一轮声称已改、实际有遗漏):
- friend_links admin(默认 + Modern):修复
.submit()缺$elbug;onchange→x-on:change="$el.form.submit()";onsubmit→ Alpine - announcements admin(默认 + Modern):删除按钮 confirm → 表单
x-on:submit.prevent - content_review admin(默认 + Modern):批量通过/批量删除 confirm → 表单级
x-on:submit.prevent(保留动态数量提示)
red_packet Modern 详情页:
- 删除按钮上冗余的
onmouseover/onmouseout内联事件(与 CSS:hover规则重复)
4.5 本轮改动文件清单
| 类别 | 文件 | 改动 |
|---|---|---|
| 个人中心 | app/Views/profile/index.php | Tab 切换 Alpine 化,删内联 script |
| QA 控制器 | plugins/qa/QaController.php | 5 方法加 htmx 分支 + 全部错误分支 htmx 响应 |
| QA 前台 | app/Views/plugins/qa/question/show.php + Modern 版 | 投票/采纳/回答表单 htmx 化,删 script |
| QA 前台 | app/Views/plugins/qa/question/ask.php + Modern 版 | 提问/编辑表单 htmx 化,删 script |
| QA 后台 | app/Views/plugins/qa/admin/index.php、tags.php + Modern 版 | 修 $el bug + onsubmit → Alpine |
| 审核通知 | app/Views/plugins/content_review/notifications.php + Modern 版 | 删死代码 / Alpine 化 |
| 收藏 | app/Views/plugins/post_favorite/index.php + Modern 版 | 取消收藏 htmx 化 |
| 收藏 | plugins/post_favorite/FavoriteController.php | toggle 加 htmx 分支 |
| 收藏 | plugins/post_favorite/assets/script.js | 删除已 htmx 化的 fetch 部分 |
| 友链后台 | app/Views/plugins/friend_links/admin.php + Modern 版 | 修 $el bug + Alpine 化 |
| 公告后台 | app/Views/plugins/announcements/admin.php + Modern 版 | 删除 confirm Alpine 化 |
| 审核后台 | app/Views/plugins/content_review/admin.php + Modern 版 | 批量 confirm Alpine 化 |
| 红包 | assets/themes/modern/templates/plugins/red_packet/detail.php | 删除冗余内联 hover 事件 |
| 资源目录 | ziyuan/ | 确认资源已复制后整目录删除 |
5. 综合踩坑记录(三轮合并去重)
以下坑点来自:重构计划(9.1-9.7)、第一轮会话总结(6 条)、第二轮补全总结(11 条),已合并去重并按重要性排序。
5.1 $el. 丢失 bug(两轮反复出现)⚠️ 最高优先级
症状:confirm 表单写成
x-data x-on:submit.prevent="if(!confirm('确定删除?')) return; .submit()".submit() 前丢了 $el,删除按钮直接失效。
原因:用正则批量替换 onsubmit="return confirm(...)" 时,$el 被当作正则捕获组 $1 引用吞掉。
第一轮声称"逐个修复受影响文件",但 QA admin 和 friend_links admin 漏网,第二轮才发现。
解决:逐文件补齐 $el.submit()。
教训:正则批量替换 + 含 $ 的内容 = 灾难,替换后必须 grep '\.submit()' 全库复核 $el 完整性。
5.2 CSP 安全策略缺 'unsafe-eval'
症状:Alpine 下拉菜单、弹窗、x-data 完全不工作,浏览器控制台无任何报错。
原因:服务器 CSP 头缺少 'unsafe-eval',Alpine 的表达式解析依赖 eval。
解决:app/Core/Controller.php 的 script-src 中加入 'unsafe-eval'。
5.3 x-show 与 CSS display:none 冲突
症状:用 x-show 控制显隐的元素一直不显示。
原因:现有 CSS 中 .dropdown-menu { display: none; } 等规则权重高于 Alpine 的内联 display。
解决:改用 :class 在父容器上绑定类名,由现有 CSS 机制控制显隐;不要直接 x-show 被 CSS 控制的元素。
5.4 htmx 错误提示容器不能预置 display:none
症状:回答/提问表单的错误容器 <div id="qaAnswerError" style="display:none;"></div>,
htmx 把错误片段 swap 进容器后依然不可见。
原因:htmx 替换的是容器内容(innerHTML),容器自身的 display:none 内联样式还在。
解决:去掉容器上的 style="display:none"(空容器本身不占高度,无需隐藏)。
教训:htmx 目标容器不要预置 display:none 内联样式。
5.5 $refs 作用域问题
症状:$refs.navLinks 为 undefined。
原因:$refs 只能在 x-data 作用域内访问子元素;按钮的 x-data 与 x-ref 在兄弟元素上。
解决:把 x-data 移到共同父元素上,用 :class 替代 x-ref。
5.6 个人中心 Tab 全部显示(作用域问题变体)
症状:三个 Tab 内容同时显示。
原因:x-data 放在 Tab 导航上,x-show 的 Tab 内容在同级位置,不在作用域内。
解决:把 x-data 移到共同父容器上。
5.7 投票图标不显示 / 不一致
症状:htmx 替换后投票图标消失(第一轮);Vote::renderButtons() 用 SVG 而视图用 FontAwesome,样式不一致(计划阶段)。
原因:ApiController 返回的 HTML 用了 CSS 类方式(fa fa-thumbs-o-up),项目实际用 Unicode 字符实体()。
解决:统一使用  /  Unicode 字符实体。
5.8 投票二次点击无效(htmx 缺 csrf)
症状:第一次点击投票正常,第二次点击无效。
原因:htmx 的 hx-vals 中缺少 CSRF token。
解决:在 hx-vals 中加入 csrf 参数。
教训:htmx 按钮类请求必须带 csrf(表单类可用隐藏域自动带上)。
5.9 投票按钮高亮:默认模板用 class,Modern 模板用内联颜色
症状:默认模板按钮用 class="... voted" 表达高亮;Modern 模板用
style="color:var(--mn-primary)" 内联颜色。htmx 返回的 script 若只 toggle class,Modern 高亮不生效。
解决:vote() 返回的 script 里两者都处理:
b.classList.toggle("voted", on); // 默认模板
if (b.style.color) { // Modern 模板(有内联颜色才走)
b.style.color = on ? (dir===1 ? "var(--mn-primary)" : "var(--mn-error)") : "var(--mn-text-muted)";
}5.10 Modern 主题存在"引用了未定义函数"的隐患
症状:Modern 主题 QA 详情页 script 调用 qaVote() / qaAccept(),但函数只在默认模板内联 script 中定义,点击投票 ReferenceError。
解决:本轮将投票/采纳全部改为 htmx 属性,函数彻底消失,根治。
教训:跨模板复制 JS 逻辑时函数定义和调用必须一起迁移;htmx 化可从根上消灭此类问题。
5.11 Modern 主题部分页面上一轮根本没改
症状:grep 发现 Modern 版 content_review 通知页还是原始 onsubmit/onchange,而默认模板早已 Alpine 化。
原因:两套模板都要改,上一轮只改了默认模板,Modern 漏了。
解决:第二轮将 Modern 版按默认模板同等模式改造。
教训:两套主题(默认 + Modern)必须同步检查,用 grep 把 Modern 目录也扫一遍。
5.12 猜题按钮文字乱码
症状:猜题按钮显示乱码。
原因:x-text 属性中包含 PHP 图标 HTML 代码,破坏了属性解析。
解决:移除 x-text,保留 :disabled。
5.13 骰子无效押注类型
症状:骰子投注时提示"无效押注类型"。
原因:bet_type 和 amount 控件在 <form> 外部。
解决:将控件移入 <form> 内。
5.14 默认模板变宽
症状:改造后默认模板页面宽度变宽。
原因:Tailwind 的 .container 类覆盖了原有布局的 max-width。
解决:在 tailwind.config.js 中设置 container: false(如引入了 Tailwind)。
说明:SoSite 未引入 Tailwind,此坑为规避性记录。
5.15 同一段代码出现两次导致编辑工具无法定位
症状:edit_file 报 "old_string appears 2 times" —— QaController 里 answer() 和 edit()
都以 $this->json(['success' => true, 'url' => '/qa/question/' . $id]); 结尾。
解决:用缩进区分(answer 在方法体顶层 8 空格,edit 在 if 块内 12 空格),携带方法尾部上下文精确定位。
5.16 Windows / phpstudy 环境坑
| 坑 | 症状 | 解决 |
|---|---|---|
php 不在 PATH | php -l 报"不是内部或外部命令" | 用完整路径 D:\phpstudy_pro\Extensions\php\php8.2.9nts\php.exe -l <file> |
| cmd 变量展开 | set VAR=... & %VAR% 报"不是内部或外部命令" | cmd 一行内 %VAR% 在赋值前展开;直接写完整路径,不用变量 |
| 内置服务器启动超时 | start /b php -S 让 bash 工具等到超时;PowerShell Start-Process 报"不支持输入重定向" | 放弃自建服务器,直接用 phpstudy 已在 80 端口运行的 nginx 做 curl 测试 |
5.17 ziyuan 与 assets/lib 文件"不一致"的误判
症状:fc /b 显示 ziyuan/alpine/alpine.min.js 与 assets/lib/alpine.min.js 不同,差异字节达 44452 个。
原因:assets/lib 版文件头多了一段 SoSite 项目注释(约 150 字节),加上行尾符(CRLF/LF)差异,
按字节逐位比较几乎处处不同。
解决:用 PowerShell 规范化换行后验证 ziyuan 内容完整包含于 assets/lib 版($lib.Contains($ziyuan) = True),
确认 lib 版只是"加注释版"、功能一致,且全库无代码引用 ziyuan 路径,才安全删除。
教训:文件"看起来不一样"不等于内容不一样,比较前先考虑注释头、行尾符,用"包含关系"验证而非逐字节比较。
5.18 错误日志中疑似 bug 的排查
症状:全站测试时 protected/error.log 出现 QuizDuel accept error: SQLSTATE[HY093]。
排查:该日志是 7-19(改造前一天)的历史记录;当时 SQL 用 :now 占位符而绑定参数名不匹配,
当前代码已改为 :accepted_at / :completed_at 且绑定正确 → 已修复,非本轮引入。
6. 综合改造对照表
6.1 内联事件替换对照表
| 当前模式 | 替换为 | 计划数量 | 实际完成 |
|---|---|---|---|
onsubmit="return confirm('...')" | x-data x-on:submit.prevent="if(!confirm('...')) return; $el.submit()" | 30 处 | ✅ 两轮完成(含后台遗漏修复) |
onclick="closeModal()" | x-on:click="open = false" | 10 处 | ✅ 第一轮 |
onclick="toggleNav(event)" | x-on:click="$refs.nav.classList.toggle('open')" | 4 处 | ✅ 第一轮 |
onclick="showTab('info')" | x-on:click="tab = 'info'" | 3 处 | ✅ 两轮(含默认 profile) |
onclick="toggleVote(this, 1)" | hx-post="/api/vote" hx-vals='{"vote":"1"}' | 8 处 | ✅ 第一轮 |
onchange="toggleAll(this)" | x-on:change="document.querySelectorAll(...).forEach(...)" | 4 处 | ✅ 两轮 |
onchange="this.form.submit()" | x-on:change="$el.form.submit()" | — | ✅ 第二轮 friend_links |
onmouseover/onmouseout(冗余) | 删除(CSS :hover 已有) | — | ✅ 第二轮 red_packet |
6.2 AJAX 替换对照表
| 功能 | 当前方式 | 替换为 | 完成 |
|---|---|---|---|
| 骰子投注 | XMLHttpRequest + JSON | hx-post="/dice/play" + HTML 片段 | ✅ 第一轮 |
| 创建挑战 | XMLHttpRequest + JSON | hx-post="/duel/create" + HTML 片段 | ✅ 第一轮 |
| 应战 | XMLHttpRequest + JSON | hx-post="/duel/accept" + HTML 片段 | ✅ 第一轮 |
| 签到 | 表单 POST + 页面刷新 | hx-post="/checkin" + 局部刷新 | ✅ 第一轮 |
| 投票 | XMLHttpRequest + JSON | hx-post="/api/vote" + HTML 片段 | ✅ 第一轮 |
| QA 投票 | onclick=qaVote() + XHR | hx-post="/qa/vote" + HTML 片段 | ✅ 第二轮 |
| QA 采纳 | onclick=qaAccept() + XHR | hx-post="/qa/accept/ID" + hx-confirm | ✅ 第二轮 |
| QA 回答/提问 | fetch + JSON | 表单 hx-post + HX-Redirect | ✅ 第二轮 |
| 收藏取消 | fetch + JSON(script.js) | hx-post="/favorite/toggle" + outerHTML | ✅ 第二轮 |
7. 全站功能验证(两轮汇总)
7.1 PHP 语法检查
- 第一轮:改动文件全部通过
php -l - 第二轮:28 个改动文件全部
No syntax errors detected
(含 profile、QA 前台/后台、content_review、post_favorite、friend_links、announcements、red_packet)
7.2 页面访问测试(第二轮,通过 phpstudy nginx + curl)
| 结果 | 路由 |
|---|---|
| ✅ HTTP 200 | / /forum /login /qa /blog /tags /qa/tags /theme-settings /register /forgot-password /blog/category/1 |
| ✅ HTTP 302(需登录,正常跳转) | /redpacket /favorites /profile /search /qa/ask /checkin /dice /admin /duel /duel/create /message /links/apply |
| 说明 | /quiz_duel 404 是路由路径问题——猜题实际路由为 /duel(非 bug) |
7.3 错误日志
protected/error.log无 08-04 ~ 08-06 的新错误- 仅存的
QuizDuel accept error为 7-19 历史记录,对应 SQL 参数问题当前代码已修复
7.4 资源完整性确认(删除 ziyuan 前)
alpine.min.js/htmx.min.js的 ziyuan 版完整包含于assets/lib/版(仅多头部注释 + 行尾符差异)app/、assets/全库无任何ziyuan路径引用
8. 备份与回滚
- 第一轮与第二轮改动前均做备份,最新完整备份位于
_backup_20260806/
(含被删除的 ziyuan/ 目录、profile、全部插件视图/控制器、thread 页、ThreadController 等)
- 回滚方式:将备份目录中的文件按原路径覆盖回去即可
9. 核心原则(下次改造必读)
- 只解决实际问题,不为技术而技术
- 不改 CSS 类名——
mn-*体系是自洽的,不需要换 - 不改内联样式——动态 PHP 颜色保留
- 不改编辑器——CKEditor 保持独立
- 不改图标系统——FontAwesome 保持原样
- 每次改完跑一遍全站——确认功能正常再继续
- 不要引入任何构建工具链——SoSite 是 PHP 项目,不需要 npm/Webpack/Vite
- 如果某个改动只是"换了个写法"但没有实际收益,那就不要做
- 两套主题(默认 + Modern)必须同步检查——上一轮遗漏几乎全部集中在 Modern
- 含
$el的代码绝不能用正则批量替换——替换后必须 grep 复核 - htmx 按钮请求带 csrf(hx-vals),表单用隐藏域;目标容器不要预置 display:none
- 验证三层都要过:
php -l+ curl 页面 + 查 error.log
10. 附录:Alpine.js / htmx 常用指令速查
10.1 Alpine.js 常用指令
| 指令 | 用途 | 示例 |
|---|---|---|
x-data | 定义组件数据 | x-data="{ open: false }" |
x-show | 显隐控制 | x-show="open" |
x-text | 设置文本 | x-text="count" |
x-on:click | 点击事件 | x-on:click="count++" |
x-on:submit.prevent | 表单提交(阻止默认) | x-on:submit.prevent="if(!confirm('确定?')) return; $el.submit()" |
:class | 动态 class | :class="{ 'active': tab === 'info' }" |
x-ref | 引用元素 | $refs.myEl |
x-on:click.outside | 点击外部 | x-on:click.outside="open = false" |
x-on:keydown.escape.window | 键盘事件 | x-on:keydown.escape.window="open = false" |
x-init | 初始化逻辑 | x-init="if (location.hash) tab = location.hash.substring(1)" |
$el | 当前元素引用 | $el.submit() |
10.2 htmx 常用属性
| 属性 | 用途 | 示例 |
|---|---|---|
hx-get | GET 请求 | hx-get="/api/data" |
hx-post | POST 请求 | hx-post="/api/submit" |
hx-target | 目标元素 | hx-target="#result" / hx-target="closest .fav-item" |
hx-swap | 替换方式 | hx-swap="innerHTML" / hx-swap="outerHTML" |
hx-trigger | 触发方式 | hx-trigger="click" |
hx-vals | 附加数据 | hx-vals='{"key":"value","csrf":"..."}' |
hx-confirm | 确认弹窗 | hx-confirm="确定?" |
hx-indicator | 加载指示器 | hx-indicator="#spinner" |
响应头 HX-Redirect | 服务端重定向 | header('HX-Redirect: /qa/question/5') |
| 判断条件 | 服务端识别 htmx 请求 | !empty($_SERVER['HTTP_HX_REQUEST']) |
11. 后续建议
- post_favorite 帖子页收藏按钮:目前仍依赖
script.js动态注入 + fetch。
若要彻底 htmx 化,需要 ThreadController 传入 isFavorited、thread 页服务端渲染按钮、
toggle 返回按钮 HTML —— 属于较大改动,建议单独排期
- QA 前台问答列表/标签页:已检查为纯展示,无需改;若后续加"热门/未回答" Tab 交互,直接用 Alpine
- 增加轻量 JS 规范 lint:扫描
onclick=/onsubmit=/XMLHttpRequest/fetch(/<script>残留,
纳入日常自查脚本,防止回潮
editAnnouncement()/editLink()等模态框填充函数:已与 Alpine 模态框配合
(Alpine.$data(...).open=true),属于模态框配套逻辑,保留合理;若追求极致统一可后续改为 x-data 直接承载
- 删除旧 layout 中废弃 JS 引用:确认
vote.js/nav.js引用已从两个 layout 移除
文档结束 — 覆盖 计划 → 第一轮(07-20)→ 第二轮(08-06)完整过程,备份位于 _backup_20260806/