博客
工程

个人网站重构记录

一次个人网站的整理:页面与内容、Fumadocs 阅读体验、Meshy 生成的 3D 茄子,以及代码和部署流程的调整。

· 7 min read

最近集中整理了一轮 personal site。最开始是想调整首页的样式,后来把文章、笔记、代码结构和部署流程也一起改了。首页的茄子则从原来的液态效果,换成了用 Meshy AI 生成的 3D 模型。

这篇记录一下过程,以及几个比较有意思的取舍。

页面先做减法

旧版用了比较多的紫色,首页还有液态渐变。单独看效果还不错,但放到整个页面里,背景、文字和动画都有自己的存在感,内容的层次反而不明显。顶部和底部两套导航也有些重复。

这次把底色换成暖白和近黑,紫色保留在链接、选中状态和首页模型上。导航合并,首页各部分的间距收紧,让介绍、文章和经历能连续读下来。

视觉上主要是删东西。个人主页需要一点自己的特点,但大部分区域还是留给内容比较合适。

内容也顺便整理了一遍。以前的文章在 Hexo 和 Notion,技术笔记又是另一套组织方式,想找一篇自己写过的东西,有时还要先想它放在哪里。现在把适合公开的内容逐步迁回来,旧 Hexo 链接保留跳转,博客和笔记则分别组织。

博客和笔记用了 Fumadocs

阅读部分接入了 Fumadocs。侧栏、正文目录、搜索和代码块这些能力比较成熟,自己重新做一套会花不少时间,而且手机和宽屏的适配也要一起考虑。

项目里由 fumadocs-mdx 编译内容,fumadocs-core 提供内容源、导航和搜索能力,fumadocs-ui 负责阅读布局。代码高亮用了 Shiki。实际写作时,博客还是写 MDX,笔记写 Markdown,文件直接放在仓库里。

接进来以后,也不是套上默认页面就结束了。博客更适合按分类浏览,笔记需要按主题展开;桌面可以同时放侧栏和目录,手机上则要收起来。两类内容共用阅读组件和搜索入口,再按各自的组织方式做适配。

正文放在仓库里的一个好处是,修改可以直接看 diff,也可以和相关代码一起提交。不过文章的发布状态、可见性、精选和阅读数,仍然需要单独管理,所以这部分留在 D1。服务端读取时,再把正文和这些状态组合起来。

这里花了些时间统一规则。比如一篇文章设置为不在公开列表中展示,搜索、RSS 和 sitemap 也应该遵守同样的设置,不能只是博客列表里看不到,换个入口又找出来了。现在公开文章进入这些入口,不公开列出的文章只通过链接访问,私密文章和草稿则返回 404。

数据库里原来的正文列还留着兼容,但页面已经只读取仓库里的正文,避免以后出现两份内容不一致的问题。

首页的茄子换了一个做法

之前首页的茄子是用 WebGL shader 画出来的。通过距离场拼出轮廓,再加上高光和流动的颜色,鼠标划过时会产生扰动。这个方案比较轻,效果也有意思,不过外观还是偏抽象。

后来想换成更具体的形象,就选了 Jellycat 毛绒茄子作为外观参考:紫色布料、绿色叶帽,还有笑脸。用 Meshy AI 从参考图片生成模型,再导出带贴图的 GLB,放到页面里试效果。

现在 AI 做这种展示模型确实方便了很多。对于个人网站需要的一件素材,可以先得到一个完整的模型,转着看看,再判断是否适合页面。形状和贴图不用全部从零开始制作,生成出来的质感也不错。

不过模型生成以后,还有一段网页接入的工作。原始 GLB 大约 7.79 MB,直接放到首页偏大。后来把内嵌贴图最大边缩到 1024px,文件降到了约 1.74 MB,几何部分没有改。

网页里使用 Three.js 的 GLTFLoader 加载。加载后先根据包围盒把模型移到中心,按高度归一化,再根据首页空间设置大小和位置。这样不用依赖模型导出时的原始坐标和尺寸,也方便处理手机布局。

材质和灯光也调了一轮。保留模型自带的颜色和法线贴图,把金属度设为 0、粗糙度设为 1,再配柔和的主光、补光和轮廓光,让表面更接近布料。现在的毛绒感主要来自贴图和渲染设置,没有另外模拟单根绒毛。作为首页展示,这个程度已经比较符合预期。

能转,但怎么转也需要想一下

模型接上以后,交互也试了不同方向,最后确定为:按住拖动时旋转,松手保留当前角度。

横向可以连续转圈,纵向只做小幅倾斜。没有旋转惯性,也不会松手后自动回正。未操作时保留一点漂浮和摇摆,抓住时暂停。

这个行为很简单,实现时却有些细节。首先要判断用户是不是真的点到了茄子,而不是画布里的空白区域。这里用了 Three.js 的射线检测,命中模型网格以后才开始旋转。再通过指针捕获,让拖动超出模型范围以后仍然能继续。

倾斜也不能只限制一个固定角度。手机上的空间更紧,模型转动以后可能碰到导航或文案,所以还要根据投影后的边界检查安全区域。窗口失焦、页面隐藏或者尺寸变化时,也需要解除抓取,避免交互停在一个错误的状态里。

键盘控制一起保留了,方向键可以调整角度,也可以手动回正。模型可以作为装饰,但既然提供了交互,就尽量让不同操作方式都能用。

把展示效果的开销控制住

首页增加 3D 以后,性能上的取舍变得更具体了。模型和渲染依赖都有加载成本,也会持续占用 GPU。

现在会先显示首页内容,再延迟加载 Three.js 和模型。桌面像素比限制在 1.5,手机限制在 1;动画调度分别控制在 60 帧和 30 帧。离开可视区域或者切到其他标签页时暂停渲染,开启减少动态效果时关闭自动漂浮和摇摆,手动旋转仍然可用。

另一个容易漏掉的是资源释放。离开页面以后,几何体、材质、纹理和渲染器都需要清理,事件和 observer 也一样。模型又是异步加载的,还得考虑页面已经卸载、模型才返回的情况。

这部分后来拆成了几个职责明确的模块:React 负责 DOM 和加载状态,渲染器负责场景与资源,交互控制器负责指针和键盘事件。调灯光或改拖动行为时,不用再从一个很长的组件里找逻辑。

顺便整理代码和发布流程

其他功能也做了类似的调整。写作、个人经历、链接预览和茄子分别按功能组织,路由负责读取和组合页面,共用能力再放到共享目录里。主要是让后面修改时更容易找到位置,服务端的数据读取也不会混进浏览器组件。

运行环境从 vinext 调整到了 Next.js + OpenNext,继续部署在 Cloudflare Workers。开发时使用 Next.js,部署前通过 OpenNext 构建 Worker 产物。

缓存没有直接沿用最初的设想。网站会从 cookie 读取语言偏好,服务端输出对应语言的 HTML,这类页面如果进入共享缓存,就可能把一种语言的结果返回给另一种语言的访问者。因此当前没有给它们配置共享页面缓存,GitHub 数据则单独放在 KV 中缓存一小时。

预发和正式环境也分开了,使用独立的 D1 和 KV。页面改动先在 staging 的预览站确认,再进入正式环境,避免测试内容和线上数据混在一起。

检查流程这次一起补上。提交和发布前会跑类型检查、测试、构建、模块边界检查,以及实际 Worker 的冒烟检查。测试用了 Vitest 和 React Testing Library,浏览器和性能检测使用 Puppeteer、Lighthouse,在相同环境下比较结果。部署之后,再检查线上的路由和资源。

做这些主要是因为网站开始有了更多内容和交互,单靠本地打开首页看一眼,已经很难确认其他地方有没有受影响。

这轮重构之后,页面、内容和代码的组织都清楚了一些。3D 茄子是最直观的变化,写作和发布流程则是后面会经常用到的部分。网站还有些地方需要继续调整,先把这一版记下来,接下来也该往里面写点新内容了。

0 次阅读

最后更新于

评论

欢迎聊聊这篇文章。评论无需登录,审核后公开。

本页目录