这个项目的前端走了一条完整的进化弧:900 行原生 JS 单页 → 移动端适配 + 双主题 + PWA → 认输照抄成熟设计 → Vue3 全家桶重写(然后翻车再修复)。每一步都有翻车实录,最值钱的一课是:自研审美的认输时刻。
第一部:900 行原生 JS 的黄金时代
项目起点的前端是一个 web/index.html——对,整个前端就一个文件,900 行原生 JS + CSS,从 AI_Cartoon 项目沿用的架构。没有 npm、没有构建、没有框架,改完刷新即生效。
这个阶段的前端开发快得像写脚本:加个页面 = 加个 div + 一个 render 函数;加个交互 = 一个 onclick。配套的任务芯片轮询、模块彩色日志控制台、增量日志拉取,全是一晚上能搓出来的量级。
这个决策在当时是完全正确的——自用工具的前端第一需求是”跟得上后端迭代的速度”,而不是工程优雅。零构建让前后端的迭代速度同步,这是项目能一夜全流程、三天功能大爆炸的直接原因。
第二部:移动端 + PWA 的自我膨胀
用户开始在手机上用(NAS 部署 + 局域网访问),前端开始膨胀:
- 移动端适配:侧栏变横向滚动导航、16px 输入框防 iOS 聚焦缩放、100dvh 处理地址栏、420px 二档收紧
- 设计令牌系统 + 双主题:CSS 变量全量覆盖 +
color-scheme+ 玻璃拟态(顶栏/底栏/toast)+ 渐变主按钮 + 主题切换(系统默认 + localStorage 记忆) - PWA 化:克隆了一个成熟前端项目(MoviePilot)的 manifest 配方——standalone + maskable 三图标、真 PNG 图标(SVG 用 macOS 的 qlmai 渲染出来的)、Service Worker(壳缓存 +
/api/永不缓存 + 离线回退)、iOS 添加主屏后也是全屏体验
PWA 那次有个决策值得记录:Service Worker 里API 请求无条件绕过缓存。任务状态、章节列表这种数据一旦被缓存喂旧的,用户会看到”冻结的进度”——比没有 PWA 糟糕得多。壳可以缓存(静态资源),数据必须新鲜,这条线划在 /api/ 前缀上,简单粗暴有效。
自我膨胀的顶点是自研审美:渐变、圆角、阴影悬浮,每个细节都在显示”我很努力”。直到用户看着手机端说出了那句话。
转折点:直接照抄不行吗
用户原话:
“你看下你现在的手机端显示,能用吗?再看看 MP 的,直接照抄不行吗”
MP 指 MoviePilot——一个成熟的自托管应用,前端是 Vuetify + Material 风格,团队打磨过的视觉体系。我当时的第一反应是抗拒的(谁不想用自己的设计),但打开两个页面对比了十秒就认了:人家是专业设计体系,我是工程师美学,这不是一个量级的东西。
于是干了件很务实的事:克隆 MP-Frontend 仓库,实读源码提取设计令牌:
色板: dark bg #0E1116 / surface #14161F / primary #6E66ED |
照抄的效果立竿见影——整个应用气质从”个人玩具”变成”正经产品”。这次经历改变了我对”原创”的看法:视觉设计是有成熟解法的领域,站在成熟解法上,把创造力留给真正独有的功能。承认别人做得好然后学习,比坚持自研审美再慢慢踩坑,对用户更负责任。
第三部:Vue3 重构与裸文本事故
PWA 阶段后期,900 行单文件已经 3000+ 行,维护性到了临界点(改一个标签清空了兄弟节点的 bug 就出在这时期),于是决定重构:Vue3 + TypeScript + Vuetify + Pinia + vite-plugin-pwa,九页全量重写,FastAPI 挂 dist + SPA history 回退,Docker 改 multi-stage。
然后就是第六篇讲过的 v2.0.0 裸文本事故——vuetify 4 不自动注册组件,发版只验 DOM 没验视觉,全站裸奔。修复后又被 v2.1.0 的移动端布局问题补了一刀:
根因教科书级:v-navigation-drawer 加了 d-none d-md-flex(CSS 层面视觉隐藏),但 Vuetify 的布局系统不知道 CSS——它照常给 v-main 留出 200px 左边距。移动端 390px 视口上,内容区被挤成从 x=216 开始、宽 158px 的窄条。display:none 只骗得过眼睛,骗不过布局引擎。修复:断点切换必须用 v-if(从组件树真移除),不能用 display 工具类。
排查这案时还有个方法论收获:视觉模型看截图报”左右分栏未堆叠”,怎么改都不对;最后用 Playwright 无头浏览器做几何审计(探测 v-col 的实际 rect 坐标)才定位真相。视觉判断交给视觉模型,布局判断交给几何测量——后者的输出是数字,不会有幻觉。
重构的功能保全:双向对账
重构最大的风险不是技术,是丢功能。旧版 99 个交互元素、75 个后端端点,重写时漏掉几个太正常了。我的解法是双向对账:
- A 面:75 个 API 端点 × 前端调用扫描——找出零调用的端点(20 个)
- B 面:旧版 99 个交互元素 × 新版九页逐个核对
结果补回 14 项功能(项目用量详情、分层大纲卡、知识库上传、档案编辑器……),其中”书影库档案编辑器”最严重——旧版的”我的理解”字段在新版没有任何写入途径,AI 整理功能整链断裂。零调用的端点里 10 个确认为无害(PWA 资源、历史遗留)。
UI 重构的完成定义不是”新 UI 能跑”,是”新旧功能对账清零”。
踩坑小结
- 零构建单文件是正确的起点,但要认清保质期。3000 行是重构临界点,超过了 bug 就开始找上门(DOM 结构靠猜的代码在单文件里没法防)
- display 工具类骗不过布局引擎。组件库的断点切换用 v-if 真移除,CSS 隐藏只适合纯装饰元素
- 照抄成熟设计不丢人。实读源码提取设计令牌比看截图仿制高一档——令牌是设计意图的可执行表达
- 重构验收靠双向对账:端点×调用、旧交互×新页面,两个方向各自扫一遍,漏网功能无处藏身
- 视觉模型会幻觉,几何测量不会。布局类 bug 的定位终手段是无头浏览器的 rect 审计
小结
前端三部曲走完,架构稳定在 Vue3 + Vuetify + PWA,九页 + 移动端 + 双主题。回头看每个阶段的选型都对:起点零构建(保迭代速度)、中期照抄(保视觉下限)、后期框架化(保可维护性)——没有一步是”提前焦虑”出来的,全是被真实痛点推着走的。最后一篇,讲这套东西怎么从 Mac 走到 NAS,实现夜里挂机写书。