封面

笔记库与博客迁移升级笔记(DeepSeek Harness 驱动)

写作时间:2026-08-16 12:00:00
📁 博客
# Obsidian # 博客 # Astro # 迁移记录

笔记库与博客迁移升级笔记(DeepSeek Harness 驱动)

本文档记录 2026-08 由 DeepSeek Harness(AI 智能体)协助完成的「Obsidian 笔记库 + 个人博客」整合迁移全过程。 从 Hexo 半成品博客 + 双文件夹维护,升级为「一个库、一键发布」的现代静态站。 当前主线:Astro 5 + XinghuisamaBlogs 主题移植版Blog/,已转正;Blog-Astro2/ 为转正前存档副本);Hexo → Fuwari → Next.js → Astro 四代演进的完整记录见下文。

一、起点与痛点

迁移前
笔记 Zhu-OKOL-vault(Obsidian 库,fast-note-sync 自建服务器多设备同步)
博客 BlogProjects(Hexo 8 + Butterfly,部署 GitHub Pages)
痛点 写完笔记要为博客改格式、改图片路径;两个文件夹各管一摊;博客是半成品(无评论/统计/友链/关于页)
目标 笔记写完 → 加一行标记 → 双击一键发布;内容与工程都在一个同步文件夹里

二、第一阶段:合并 + 自动化发布管线(Hexo 时代)

2.1 目录整合

  • 博客工程整体迁入库内 Blog/(旧版后来归档为 Blog-Hexo/);
  • Hexo source_dir 指向库根隐藏目录 .blog_staging/——生成的博客文章与图片不进 Obsidian 索引、不参与同步
  • fast-note-sync 增加排除项:Blog/node_modulesBlog/publicBlog/.deploy_gitBlog/db.json.blog_staging

2.2 发布管线 publish.js

  • 扫描库内所有 .md只有 frontmatter 里 published: true 的笔记才发布(白名单机制,随手记永不误发);
  • 自动处理:图片复制 + 路径改写(含空格转 %20)、封面转换、> [!NOTE] 呼出框、<mark>高亮</mark>注释 删除、wiki 图片嵌入、wiki 链接转纯文本、scl 代码块映射 pascal 高亮;
  • 一键脚本 发布到博客.bat:同步笔记 → hexo clean/generatehexo deploy

2.3 数据修复(过程中发现的历史问题)

  • C语言实验报告全篇损坏:约 135 处 /assets/ 碎片污染(printf 被切成 print/assets/),逐行重建代码与 42 个图片链接;
  • 《我学会了如何搭建个人博客》误删:从旧版 public/ 渲染 HTML 逐字还原(3408 字符校验一致);
  • 200smart 各章 cover 路径修复(去掉 Obsidian 里失效的 / 前缀)、第 9/10 章补齐 frontmatter。

三、第二阶段:迁移 Astro + Fuwari

3.1 为什么换

  • Hexo 架构老化(EJS/Pug 模板、插件生态停滞),做现代定制别扭;
  • 想要:全屏欢迎页、现代卡片、搜索、暗色、后续迁到自己服务器+域名;
  • 内容与框架解耦(笔记在 Obsidian,管线可迁移),风险可控,旧工程保留作回退。

3.2 主题选择

  • 调研 RyuChan(Frosti 二创)与 Mizuki(MD3 风格)后,选定 Fuwari 打底:中文支持、明暗切换、Pagefind 搜索、Expressive Code 代码块、admonition 提示框、Photoswipe 灯箱。
  • 沙箱环境网络受限时的获取技巧:GitHub 直连不通 → 用 npm pack <tarball-URL> 走 npm 通道下载 → 自写 PowerShell tar 解析器解包。

3.3 publish.mjs 适配要点

  • Fuwari 的 published 字段是日期z.date()),YAML 里必须不带引号,否则被解析成字符串导致 schema 报错;
  • category 只支持单值,取 categories 最后一级;image 字段即封面(/assets/... 由 ImageWrapper 自动补 base);
  • 正文图片要显式写带 base 的完整路径(Astro 不给 Markdown 图片补 base),封面则相反(组件会补,写根相对即可)——这个不对称是踩坑试出来的。

四、第三阶段:美化与功能(参考多主题源码)

4.1 首页全屏欢迎(Butterfly full_page 思路)

  • 首页横幅 100vh 全屏:大标题 + 打字机副标题(heroTypewriter 循环)+ 向下滚动箭头 + 底部波浪(SVG 动画);
  • 滚动折叠效果:横幅固定于视口、内容上滑盖住、欢迎文字渐隐上移,上滑恢复(对照 Butterfly background-attachment: fixed 的等价实现);
  • 打字机文案在 src/config.tsheroTypewriter 数组里改。

4.2 佳代子背景图系统(4+2 个槽位)

槽位 路径 用途
首页横幅主图 public/images/banner.jpg 全屏第一屏
横幅轮播 public/images/banner-2..8.jpg 每 10s 淡入淡出
全站背景 public/images/background.jpg 固定图层 + 42% 暗化
封面兜底池 public/images/covers/ 无封面文章按标题哈希分配
404 配图 public/images/404.jpg 404 页
头像 src/assets/images/avatar.jpg 侧边栏 + 音乐碟片封面

4.3 性能优化(流畅度的关键)

  • 卡片毛玻璃模糊是滚动卡顿主因 → 卡片改纯半透明,模糊只留给导航栏;
  • 全站背景用独立 position: fixed 图层 + translateZ(0) GPU 合成,避免滚动重绘。

4.4 音乐播放器(本地音乐 + APlayer)

  • 早期 MetingJS(网易云歌单)方案因 CDN/接口依赖不可靠被弃用;
  • 终版:public/music/ 放本地音频(歌手 - 歌名.mp3 自动解析),构建时扫描成歌单;
  • 形态:左下角固定迷你碟片(点击播放/暂停)→ 悬停自动展开(进度/控制/歌单)→ 移开折叠;
  • 模式:单个按钮三态循环 顺序 → 随机 → 单曲循环(内部驱动 APlayer 隐藏按钮切换 order/loop)。

五、踩坑记录(重要)

现象 根因与解法
双击 bat 闪退 窗口一闪而过 脚本是 LF 换行,cmd 解析错乱;必须 CRLF + UTF-8 无 BOM
博客图片出现 assets/assets 双层目录 图片 404 库内图片本身带 assets/ 前缀,复制时需剥掉一层
已删除文件反复"复活" 生成的 demo 文章删了又出现 fast-note-sync 把服务器旧状态拉回;生成目录必须加入同步排除
音乐按钮点了没反应 无任何报错 define:vars 的内联 <script> 被 Astro 包进函数体,import 语句成语法错误;改用「JSON 数据块 + 提升的模块脚本」模式
构建报 @apply link 类不存在 Tailwind 报错 跨文件 @apply 依赖处理顺序;在用到自定义类的文件里加本地副本 + @tailwind components 指令
Astro 构建需子进程 沙箱内 EPERM 构建器要 spawn 子进程,本地环境无此限制

六、当前工作流(三步)

  1. Obsidian 写笔记,图片直接粘贴(自动进 assets/<笔记名>/);
  2. frontmatter 加 published: true
  3. 双击 Blog/发布到博客.bat:同步笔记 → Astro 构建(含 Pagefind 索引)→ 产出 Blog/dist/
  • 本地预览:Blog/本地预览.bathttp://localhost:4321/
  • 相关文档:博客发布工作流、obsidian-fast-note-sync部署笔记、Blog/README.md

六点五、第四阶段:XinghuisamaBlogs 主题 → Astro 5 移植(2026-08,先 Blog-Astro2 后转正为 Blog)

6.5.1 为什么再来一次

  • Blog-Next/(Next.js 16 + React 19 + Tailwind 4 的 XinghuisamaBlogs 本地化副本)视觉与交互最满意,但 Next 需服务器运行(API 路由/SSR),不利于纯静态部署到自己的京东云服务器;
  • 决策:把 XinghuisamaBlogs 的功能与视觉 1:1 移植到 Astro 静态站,保留「笔记一键发布」管线。

6.5.2 移植要点

  • 组件原样搬:SplashScreen(sessionStorage 记住已看)、BackgroundSlider(10s 轮播)、Navbar(含手机轮盘)、CloudPlayer/LyricBar/SidebarLyric/FloatingPlayer、工具箱计算器等,next/link → <a>usePathname/useRouter → window.location/history
  • next-themes 换轻量实现:BaseLayout 内联脚本渲染前读 localStorage.dark 类,ThemeToggleBlock 自给自足(不再需要 React Context);
  • 音乐播放器架构重写:Astro 每个 React 组件是独立 island、Context 跨 island 失效 → 把播放内核下沉为模块级单例(src/lib/music-engine.ts,一个 <audio> + useSyncExternalStore),所有播放器组件订阅同一状态;数据源按既定方案改为前端直连 https://api.injahow.cn/meting/ 网易云接口;
  • 渲染管线 1:1:文章不再走 Astro 内容集合,而是 fs 直读 + gray-matter + 与 Next 版完全相同的 unified 管线(remark-gfm/math、rehype-highlight atom-one-dark、rehype-katex、连续空行保护),构建时预渲染成静态 HTML——Pagefind 因此能索引到正文
  • PageTransition 组件 → 纯 CSS.page-enter):避免把整页静态内容拖进 island 导致客户端渲染、搜索索引失效;
  • 新增 Pagefind 站内搜索/search,postbuild 自动索引 dist/,中文 --language zh);
  • 新增标签云页 /tags(标签 + categories 最后一级分类双维筛选,/tags/<name> 深链);
  • Gitalk 占位默认关闭:配置为空显示「留言板暂未开启」卡片,填好配置后动态加载 Gitalk。

6.5.3 踩坑记录(Astro 移植专场)

现象 根因与解法
React Context 跨 island 失效 播放器组件互不同步 Astro 中每个 React 组件是独立 island;改用模块级单例 + useSyncExternalStore
把整页塞进一个 island 构建成功但 Pagefind 搜不到正文 island 内容变成客户端渲染壳;正文必须静态渲染,交互组件用 client:load 局部挂载
<script src="/pagefind/..."> 构建报错 Astro 提示 public 资源不能被打包 给 script 加 is:inline,保持原样输出
Vite 报"依赖无法解析" 一连串 resolve 失败 实为沙箱禁 spawn(vite 要起 realpath.exe/worker 子进程);本地环境无此问题
src/content/ 自动生成集合弃用警告 构建日志提示 deprecated 显式写 src/content.config.ts 声明 posts/chatters/moments/about 四个集合(渲染仍走 fs+unified 管线,两者互不干扰)
嵌套页面相对路径写错 Could not resolve "../components/..." src/pages/tags/src/pages/chatter/ 下引用要 ../../ 起步

6.5.4 待办

  • 部署到京东云服务器(备案域名已就绪),astro.config.mjssite 占位要改;
  • 佳代子图片就位后替换 public/ 的 bg1~3/avatar/default-cover/favicon/404 槽位。

6.5.5 首轮实测修复(卡顿 + 播放失败)

问题 根因 修复
界面整体卡顿 Tailwind 4 的 backdrop-blur-xl 引用 --blur-xl 刻度,默认 24px!全站卡片 24px 大模糊 + 原主题还强制 will-change: backdrop-filter,滚动/背景轮播时每帧重采样 @theme 压缩 --blur-* 全刻度(xl=6px);删掉强制 blur 与 will-change;移动端直接关闭 backdrop-filter;波形动画 height → transform: scaleY(免 layout);装饰光晕 50px 模糊 → 10px;歌词打字机 50ms → 80ms
首页打开瞬间掉帧 13 个 island 全部 client:load 抢水合 首屏外的轮播/主题卡/数据面板/工具箱/返回键改 client:visible / client:idle
长文打开卡 第 5 章 45 张图一次性全解码 渲染管线注入 rehype 插件:正文 <img> 全部 loading=lazy decoding=async
音乐页加载很久 meting 三个镜像串行尝试,每个 12s 超时,全挂要等 36s 改为:先秒开本地烘焙歌单,后台 Promise.any 并行竞速 meting(6s 超时),拿到后无感替换;播放中不打断
音乐进度 00:00/00:00 不播放 ①单例 <audio> 不再像 Next 版由 React 绑定 src,togglePlay 忘了赋值 ②烘焙歌单用的是网易云外链(http 302 且浏览器 Referer 校验) togglePlay 播放前补 el.src = currentSong.srcfetch-music.mjs 在构建期解析 302 得到 https CDN 直链再烘焙(实测无 Referer 206 流式可播);③error 事件加防死循环(全部失败则停)

6.5.6 第二轮流畅度优化(音乐页/首页专项)

实测反馈:项目/归档/标签页流畅,音乐页特卡、首页略卡。共性在于这两页同时挂着「持续状态流 + 全屏/整卡动画」:

优化 做法
全屏实时模糊(音乐页头号元凶) 视口大小的 blur(20px) filter 每次滚动都重采样 → 改为网易云 400px 缩略图铺满视口?param=400y400),放大天然朦胧、零滤镜开销;顺带去掉全屏 backdrop-blur 遮罩与 saturate
播放状态流重渲染 timeupdate 可达 10Hz+,每次 setState 让全部播放器 island 重渲染 → 进度/时间节流到 ~4Hz,歌词变化零延迟下发
音乐页底层白做工 音乐页自带全屏封面背景,BaseLayout 增加 hideBackground 开关,关掉背后的全站背景轮播
整页位移进场动画 .page-enter 从 translateY+opacity 改为纯 opacity(整页 transform 生成超大合成层)
无效毛玻璃 被图片完全盖住的卡片(文章轮播/说说轮播/照片墙海报)去掉 backdrop-blur——模糊层被图片挡住看不见但照样每帧计算
歌单行 layout 动画 移除播放列表每行的 framer layout 投影(每次重渲染都做布局校验)
旋转碟片 .rotating-discwill-change: transform 提前提升合成层
背景轮播 交叉淡入 2000ms → 1000ms(10s 间隔不变)

经验:装饰性模糊优先用「低分辨率图放大」而不是 filter;全屏 filter 是移动端/核显的绝对禁区。

6.5.7 第三轮深度优化(首页专项 + 音乐页收尾)

优化 做法
播放状态流快慢分离 引擎状态拆慢池(歌单/歌曲/播放态/歌词/音量/模式)与快池(进度条三兄弟, 4Hz)。useMusicCore/useMusicProgress 按需订阅:播放时 4Hz 刷新不再波及 LyricBar/SidebarLyric/FloatingPlayer,只剩进度条小组件跟着动
音乐页组件树瘦身 进度条+控制按钮拆成独立 ProgressControls 子组件订阅快池;歌词高亮改由 currentLyric 文本推导(不再依赖 currentTime),主树只在歌词/歌曲/播放态变化时才重渲染
首页玻璃卡片去模糊 ProfileCard/CloudPlayer/主题卡/歌词栏/数据面板去掉 backdrop-filter,改加浓半透明(bg-white/60 等)补偿可读性;毛玻璃只留给导航栏/搜索下拉/悬浮播放器
打字机降频 歌词打字机 80→100ms,且 >24 字的长句直接整句显示(绝大多数歌词不再触发高频重渲染)
时钟停表 SiteDashboard 用 IntersectionObserver + visibilitychange:离屏/切后台即停,不再每秒空转
头像瘦身 avatar.jpg 2188×2188(1MB) → 512×512(35KB),每页必加载的资源瘦了 30 倍
首屏预加载 <link rel="preload" as="image" href="/bg1.jpg">,背景首帧不和解码抢线程
轮播图懒加载 首页文章/说说轮播与照片墙海报的封面图加 loading=lazy decoding=async

效果:首页播放音乐时的持续重渲染从「3 个 island × 10Hz」降到「1 个小组件 × 4Hz」;背景轮播切换时不再触发整页毛玻璃重采样;音乐页播放时 4Hz 刷新只波及一条进度条。教训:React 单例 store 一定要按变更频率分层订阅,快慢混池会让无关组件白重渲染。

6.5.8 第四轮深度优化(转正为 Blog/ + 架构级去重)

Blog-Astro2 复制转正为 Blog/(旧 Fuwari 工程移出库)。同步动作:Obsidian 排除与 fast-note-sync 排除里 Fuwari 时代的 Blog/public/assets 换成 Astro 的 Blog/public/images(否则 174 张生成图会被索引+同步上服务器),并清理已移走工程的失效条目。

优化 做法
ClientRouter 视图过渡 astro:transitions<ClientRouter transition:animate="none">:站内跳页不再整页重载,导航栏/背景轮播/悬浮播放器/工具箱等布局 island 不重复水合,音乐引擎模块级单例全程只初始化一次(歌单不再每页重拉);内容切换用 .page-enter 纯 opacity 淡入
首页去 React 化 ProfileCard/搜索框/文章轮播/说说轮播/主题切换卡/数据面板/开场动画/背景轮播 8 个组件全部改为「Astro 静态标记 + is:inline 原生脚本」(数据用 <script type="application/json"> 块注入,事件用 data-bound 防重绑),首页 island 从 13 → 6(其中工具箱/手机返回键还是懒水合),滚动到轮播区不再有水合卡顿
搜索页换页自愈 PagefindUI 改为动态注入 + astro:page-load 监听 + 按元素 booted 标记,ClientRouter 下反复进出 /search 都能重新挂载

经验:静态站性能的终点是把"不需要共享状态"的交互全部踢出 React;Astro 的 island 是工具不是默认值——每少一个 island,每次跳页就少一次完整的水合。

6.5.9 ClientRouter 白屏事故与修复(真浏览器回归测试)

ClientRouter 上线后出现「除主页外全部白屏」。用 Playwright + Chromium 建了 scripts/nav-test.mjs 导航回归测试逐页点击定位,根因与修复:

问题 根因 修复
换页后白屏 Astro 路由器 swapRootAttributes 会把新页面 <html> 的 class 整个覆盖到当前文档,抹掉 splash-seen(内容显隐钥匙)→ #app-mount-root 永久 visibility:hidden;且路由器按脚本文本内容去重,第二次进入同一页时内联脚本不再执行 ① head 里的 splash-seen/主题恢复脚本加 data-astro-rerun(换页后强制重跑,同一任务内恢复 class,无闪屏)② 所有去 React 化组件的内联脚本加 data-astro-rerun + 已有的 data-bound 防重绑,二次进入页面功能不丢
React #418 水合错配(每次进首页/音乐页报错) 引擎模块级状态跨页存活:SSR 渲染「加载中」骨架,客户端水合时却是「已加载」→ 水合失败 音乐类组件(CloudPlayer/LyricBar/SidebarLyric/FloatingPlayer/MusicClient)全部改 client:only="react"——无 SSR 骨架可错配,且省掉服务端渲染成本
布局 island 换页重水合 路由器整体替换 body,默认不保留旧元素 Navbar/FloatingPlayer/GlobalToolbox/MobileBackButton/背景轮播加 transition:persist(真·免水合);Navbar/FloatingPlayer 加 astro:page-load 监听手动刷新 pathname(激活态小圆点、首页隐藏悬浮球跟着路由走)

配套产物:scripts/nav-test.mjs(Playwright 导航回归:逐页点击断言可见性/内容/主题/搜索/开场/音乐加载,20 项检查全过),跑法 node scripts/nav-test.mjs(需 npx playwright install chromium)。教训:上 ClientRouter 必须真浏览器回归——白屏在 curl/静态检查里完全看不见。

6.5.10 ClientRouter 回退,MPA 朴素深优化定版

实测 ClientRouter 在真机上仍有白屏闪烁、个别页面交互不良——回退整页导航(MPA),Blog 恢复为 Blog-Astro2 基线。保留的深优化只做「看不见的减法」,不引入任何新机制:

措施 说明
去 React 化(重做,不带任何路由标记) 开场动画/背景轮播/信息卡/搜索框/文章轮播/说说轮播/主题卡/数据面板 → Astro 静态标记 + is:inline 原生脚本。首页 island 13 → 6,其余页面完全不动
保留第三轮全部优化 blur 刻度压缩、歌词节流、快慢双池、懒加载、头像压缩、时钟停表等原样保留(都在回滚基线里)
回归测试 scripts/mpa-check.mjs(Playwright):全页导航逐页断言 + 搜索/轮播/主题交互 + 新标签直连开场 + 零 JS 错误,18 项全过

定版原则:每页整页加载 = 状态永远新鲜 = 没有跨页状态同步的坑。 性能靠"减少要水合的东西"而不是"加快换页的魔法"。ClientRouter 方案留档备查(6.5.9),后续若要上,必须有真机验收 + 本节的坑全部复验。

6.5.11 跨页续播(音乐不被打断)

MPA 整页导航会销毁页面里的 <audio>,播放必然中断。修复方案(在 Blog-Astro2 实现,不依赖任何路由改动):

  • 播放状态接力:播放中持续把 {歌曲/进度/音量/静音/模式} 写入 sessionStorage(进度 1s 节流 + pagehide 精确补刀);
  • 自动续播:新页面加载歌单后读取接力状态,切回原曲、seek 到记录位置并尝试 play()(Chrome 对"同域交互过"的会话放行,实际体验近乎无缝,实测两连跳进度 3.1s → 5.5s → 7.9s);
  • 手势兜底:若浏览器自动播放策略拦截(如重启浏览器恢复标签页),进入"待手势"状态,歌词栏显示 ♪ 点击页面任意处继续播放 ♪(冻结歌词更新防覆盖),用户第一次点击/按键即续播(进度已 seek 到位);
  • 暂停语义保持:暂停后跨页仍是暂停态,不会自动开播。

回归测试 Blog-Astro2/scripts/music-continuity.mjs(Playwright,7 项):自动续播/连续两跳/暂停保持/拦截提示/手势续播/进度接续/零 JS 错误。

6.5.12 续播衔接压缩 + 悬浮播放器悬停保持

问题 根因 修复
悬浮歌单一移出就消失 播放栏与歌单面板之间有 12px 空隙,mouseleave 立即折叠 折叠延迟 350ms + 移入面板时取消折叠计时器(移入歌单点歌不再手忙脚乱)
换页"卡一下"(衔接 ~1.5s) 新页面的音频要等 React 水合 → 引擎启动 → 拉歌单 → 才开始缓冲 ①接力状态增加音频直链 ②BaseLayout head 加同步预热脚本:页面解析期就创建 <audio> 并开始缓冲(不自动播放,避免从 0 响)③引擎模块导入即启动(不等水合)④引擎接管预热元素:readyState 判定后 seek + 续播,同 src 不重复赋值打断缓冲。实测衔接 1567ms → 69ms
预热后 React #418 引擎启动早于水合,SSR"连接中"骨架与实际状态错配 音乐类组件(CloudPlayer/LyricBar/SidebarLyric/FloatingPlayer/MusicClient)改 client:only="react"(无 SSR 骨架可错配)

经验:MPA 跨页续播的物理极限是"新页首包时间 + 音频首缓冲",把音频创建提前到 head 同步脚本可把感知停顿压到 100ms 内;彻底零间隙仍需 SPA,取舍由用户定。

6.5.13 方案 B 落地:iframe 外壳,真·零间隙

用户对 ~70ms 衔接仍不满意,选择零间隙方案。调研确认:预渲染(媒体加载被推迟)/Worker(无 AudioContext 输出)/SW(不能播)/弹窗(体验差) 均不可行;可行的是 SPA 或 iframe 外壳,选后者(页面 100% 保持 MPA)。

架构(htmf self-framing 思路 + 自研)

  • 每个页面顶层加载时(window.self === window.top)自动"变身外壳":把 #site-content 内容区替换为全屏 iframe(重新加载本页地址),音频引擎与开场动画住在外壳文档;之后所有站内导航发生在 iframe 内,外壳永不卸载 → 音乐零间隙是结构保证的;
  • 音乐引擎双模式:外壳=宿主(单例 <audio> + 快慢双池 + 接力 + 跨帧广播状态/接收指令);iframe 页面=桥接(状态镜像 + postMessage 转发动作与手势),MusicProvider 与全部播放器组件零改动
  • 地址栏/标题由外壳监听 iframe load 同步(history.replaceState),前进后退经 popstate 反向驱动 iframe;深链天然支持(首次加载即变身);无 JS 环境退化为普通静态页(渐进增强);
  • 页面唯一的 module 脚本恰好是引擎引导(island 水合是内联经典脚本、找不到宿主自动空转),外壳无需删脚本、无需动态导入。

回归(music-continuity.mjs 扩到 16 项,Playwright):零间隙导航 27/43 个 60ms 高频采样 0 中断、0 进度回退(两连跳复验);暂停保持/拦截提示/手势续播(31s 接续)/悬停歌单保持与切歌/深链外壳变身/F5 手接续播/零 JS 错误——全过。

踩坑:①Vite ?url 导入 TS 模块产出的是 .ts 原样资产(MIME/解析靠不住),放弃动态导入,改用"页面唯一 module 脚本即引擎引导"这一事实;②烘焙歌单的网易云 CDN 令牌约 2 小时过期(403)——引擎增加"全曲失败现场重试 meting 换新链接"自救,日常由 bat 每次重烘焙兜底;③手势续播函数曾与挂起守卫互斥(attemptResume 首行 return 挡住了恢复调用),抽出 resumeFromGesture 供宿主监听与桥接转发共用。

已知取舍:每次外壳刷新(F5)仍走 ~70ms 接力;长时开站需定时重烘焙歌单(bat 流程已覆盖);无 JS/爬虫看到的是普通页面(渐进增强)。

6.5.14 换页黑闪消除(视图过渡 + 外壳背景兜底)

零间隙上线后出现「换页突然黑屏闪眼睛」:iframe 内层换页时旧文档销毁、新文档未画,间隙里露出的是外壳 body 背景——暗色模式 slate-950 接近纯黑。修复(双层):

做法 覆盖
跨文档视图过渡 内层 CSS 加 @view-transition { navigation: auto; }:浏览器截取旧页快照与新页交叉淡入,间隙被过渡填满,不露底 Chrome/Edge 111+(官方文档
外壳背景换站点氛围 外壳 body 换 bg1.jpg + 暗色蒙层(linear-gradient(rgba(2,6,23,.55),…), url(/bg1.jpg),经 html.shell-mode 限定只在外壳生效) Firefox/Safari 等不支持视图过渡的浏览器,露出的底也是站点自身背景

回归新增场景 7:pagereveal 事件断言内层换页时 viewTransition 存在(机制级验证)+ 外壳背景图断言,18 项全过。教训:iframe 外壳方案里"露底"是结构性的,对策是"让过渡填满间隙"(Chromium)+ "让露出的底长得像站点"(兜底)双保险。

6.5.15 跨文档视图过渡回退,双帧换页定版(彻底无闪)

视图过渡上线后换页出现随机正常/白屏/黑屏。查证为已知 bug 族:WebKit 310627「新页在过渡播放前闪烁」、280703「MPA demo 行为异常」、Chromium 41248398「换页白屏回归」;且 wpt 承认跨文档 VT 主要只测顶层上下文,iframe 内导航基本未测——正是我们的场景。结论:砍掉 VT,改结构性无闪方案。

双帧换页(不依赖任何过渡 API,全浏览器一致):

  • 内层文档加捕获阶段点击拦截:同源站内 <a>(排除按钮/输入/外链/_blank/新标签组合键)preventDefault 后发 site:nav 给外壳;
  • 外壳维护两个全屏帧:旧页保持可见,新页在隐藏帧加载;load 完成后换帧(旧淡出新淡入,300ms 交叉),旧帧延迟 400ms 回收;加载过慢有 2.5s 兜底;
  • 地址栏:外壳主动导航 pushState、帧自发导航 replaceState;前进后退经 popstate 反向驱动;返回按钮(BackButton/MobileBackButton/ProfileCard)改走外壳历史栈;
  • 音乐引擎状态改为广播到所有帧(新帧 hello 后立即拿到全量状态);
  • 修一个潜伏 bug:Astro 默认给组件 <style> 加作用域后缀,JS 创建的 iframe 无该属性导致样式全失效(早期靠内联 style 掩盖)——样式块改 is:global

回归场景 7 改为「40ms 高频采样双帧透明度之和」:换页全程 minOpacitySum = 1(任意时刻至少一帧可见,露底从机制上不可能),18 项全过。定版原则:过渡类视觉方案在 iframe 内不可靠时,退回"旧画面保留到新画面就绪"这一朴素而绝对成立的方案。

6.5.16 v1.0 归档里程碑

功能与视觉 1:1、零间隙音乐 + 无闪换页两项体验指标全部达成,定版 v1.0

  • 归档:库外 D:\工业自动化笔记\blog-archive\Blog-v1.0-2026-08-16.zip——源码快照(含归档说明.md),排除 node_modules/.astro/dist/生成内容,零 Obsidian 影响;
  • 清理:删 continuity-out/err.txt、dist/、.astro/,npm uninstall playwrightscripts/music-continuity.mjs 18 项回归脚本保留,用时 npm i -D playwright && npx playwright install chromium 临时装);
  • 目录定局Blog-Astro = 归档(不再动),Blog-Astro2 = 唯一工作目录,用户自行复制转正为 Blog;
  • README 重写:v1.0 标记 + iframe 外壳/双帧换页架构说明 + 测试用法 + 佳代子槽位表;
  • Obsidian/fast-note-sync 排除已复核(node_modules/dist/.astro/src/content/posts/public/images),归档 zip 在库外。

佳代子图片槽位(找图后放入或交给我统一裁切压缩): public/bg1~3.jpg 横构图背景轮播(宽 ≥1920、单张 <500KB)、public/avatar.jpg 方形头像(≥512)、public/default-cover.jpg 16:9 默认封面(宽 ≥1200)、public/favicon.png 图标(≥256)、public/404.jpg 404 配图。

6.5.17 佳代子图就位后的观感修正(背景静态化 + 透明度)

用户放好图后发现两个观感问题,一并处理:

  • 背景轮换移除:轮播是每个 iframe 页面各自持有一份状态、各自从 bg1 开始播 → 换页/切窗必回 bg1,不协调。删 BackgroundSlider.tsxBaseLayout 背景层改渲染静态 bgImages[0](纯 inline style div,还省掉一个 React island);siteConfig.bgImages 只留 ["/bg1.jpg"](想恢复轮播:加回 URL + 从 v1.0 归档还原组件)。
  • 透明度两连修:① 发现隐蔽 bug——压暗蒙层 z-[-9]/渐变 z-[-8] 负 z-index 排在背景图下方,蒙层从未真正盖在图上(这就是「面板和背景打架」的根因),改为不带负 z、靠 DOM 顺序自然叠加,蒙层加浓为 bg-white/55 dark:bg-slate-950/70;② 全站玻璃面板透明度集体上调一档(脚本单遍替换:bg-white/30~70 → /55~85dark:bg-slate-800/900 同步、bg-slate-50/100 内衬 上调、Pagefind 搜索卡 0.5→0.75),外壳暗色蒙层 0.55→0.65。
  • 替换按「降序处理 + 新值不与后续源值撞车」保证零重复叠加,替换后 grep 复核无残留。

6.5.18 背景蒙层回调 + 正式上线 zhu-okol.cn(京东云)

  • 蒙层回调:整页蒙层太暗(55/70),用户反馈「只调框里就行」——面板保持 55~85 不透明不变,整页蒙层降为 bg-white/10 dark:bg-slate-950/30(保留层级修复:蒙层在背景图之上,只给露在卡片外的文字兜底)。
  • 部署上线(一步到位,含 HTTPS):
    • 环境:CentOS 7 + nginx 1.20.1 + SELinux Disabled;astro.config.mjs site → https://zhu-okol.cnsiteConfig 填备案号「沪ICP备2026035957号-1」+ 友链申请格式换正式域名;
    • 文件放 /var/www/zhu-okol/(本机 tar 打包 → ssh2 SFTP 上传 → 远程解压,70MB 一次传完);
    • nginx:conf.d/zhu-okol.conf(静态资源分级长缓存 + try_files $uri $uri/ $uri.html =404 + 404 页 internal);certbot --nginx 自动写入 443 块并 301 跳 https;证书 Let's Encrypt(到期 2026-11-14),/etc/cron.d/certbot-renew 每日 3 点自动续期;
    • 两个坑:① CentOS 包自带 nginx.conf 的默认站位于 include conf.d/*.conf 之后,导致 conf.d 里的 80 块成为隐式默认站——IP 直访落到 return 404,修复 = 给 nginx.conf 默认站显式加 default_server 并 301 跳主站;② try_files 兜底 /404.html 会以 200 状态吐 404 页,改 =404 + location = /404.html { internal; } 后返回真实 404;
    • 上线验证全过:外网 http 301 → https 200、文章深链 200、pagefind 200、不存在页面 404、页脚备案号在位;
    • 新增 部署到服务器.bat(同步歌单→同步笔记→清空重建 dist→打包→上传→重载),上传凭证在库外 deploy-tools/;三个构建类 bat 全部加 rmdir /s /q dist 防旧文件残留。
    • 待办:公安网备案(beian.mps.gov.cn,ICP 通过后 30 天内建议办理)。

6.5.19 命名定案 + 换歌 + 部署脚本幂等化

  • 命名定案:英文 Zhu-OKOL / Zhu_OKOL(标题/社交/GitHub/域名),中文显示层 佳伟siteConfig.authorName 全局替换,页脚/关于页/文章作者框/说说/开场都变),平台昵称「佳伟_佳代子单推人」;站点标题保持 Zhu-OKOL の 自动化笔记
  • 换第一首歌cloudMusicIds[0] 1809646618 → 2098477275(Aira - ミツキヨ);注意歌单 ID 有两处——siteConfig.ts(运行时 meting)与 scripts/fetch-music.mjs(构建期烘焙),注释已互相提醒同步。
  • 部署脚本幂等化(重要教训):deploy.mjs 原来每次都重写 nginx 配置——第二次部署覆盖掉 certbot 写入的 443 块、又重写 default.conf 与 nginx.conf 的 default_server 冲突 → nginx -t 直接报 duplicate default server,配置进入损坏状态。修复:站点 conf 仅在不存在时写入(之后交给 certbot 管理)、不再碰 default.conf、HTTPS 自检改正确 SNI;全流程重跑一遍验证无误。结论:部署脚本对服务器配置必须幂等,站点文件随便覆盖、nginx 配置只写一次

七、后续计划

  • 迁移到自建服务器 + 备案域名(已完成:https://zhu-okol.cn,nginx + HTTPS 自动续期,部署到服务器.bat 一键上线)
  • 公安网备案(beian.mps.gov.cn);
  • 佳代子图片就位后整体观感微调(bg1/头像/默认封面/favicon/404 已就位,剩余 bg2/bg3 备用);
  • 稳定优化路线图(构思中,本版不实施):Media Session API 接管系统媒体键、hover 预建 pending frame 提前加载下一页、发布管线图片压缩瘦身、外壳记住每帧滚动位置等;
  • 留言板换自建 Twikoo(服务器已就绪);
  • 学习路线页(把三年计划做成站点导航);
  • 访问统计、友链内容填充。
💬

留言板暂未开启

评论方案(Giscus / Twikoo)将在后续版本敲定, 敬请期待。