第一篇印记 | hyporos.com 建站记:米表、博客导航与零数据库后台
2026-10-25 · 建站笔记 · 阅读约 10 分钟

一、缘起

最近手上攒了一批域名,扔在注册商后台里吃灰。在逛米表发现域名出售中,这个米表样式挺好看,就联系了一下上头的关系其实就是WorkBuddy和Zcode让他们给我做个米表挂到个人主页下面。

再者昨天在blog

三个页面用同一套样式,凑成了现在 hyporos.com 的样子。

二、站点结构

hyporos.com/
├── /            清风吟 · 个人单页(首页)
├── /mi/         米表 · 域名出售(数据 domains.json)
├── /web/        博客收录网站 · 导航(数据 sites.json)
├── /article/    米店开张 · 文章页
├── /go/         站外链接跳转中间页(来源可追溯)
├── outlink.js   三站共用的外链拦截脚本
└── /web/hyperos-login.php   导航后台(零数据库)

整站只有两个文件算得上动态,就是后台写的两个 JSON。前台全是静态 HTML 加原生 JS,没装任何框架,也没走构建流程,最便宜的纯静态空间就能跑。

三、三个前台页面

米表 /mi/

米表页面截图
米表,域名卡片两列网格,含到期时间、用途描述与价格

域名卡片两列网格,每张卡展示域名、到期时间、用途描述和价格,支持中英切换、明暗主题。邮箱做了防爬虫处理,页面里只有打散的数据片段,真实地址由 JS 动态拼出来,爬虫抓不到完整的。

博客收录网站 /web/

博客收录网站截图
博客收录网站,当前收录 30 余个中文独立博客与博客收录站

卡片式导航,三种状态,已上线、精选、暂时失联,全部数据来自 sites.json,后台增删改完刷新就生效。

四、全站一套样式

全站就一套 CSS 变量,亮色暗色各一份。切换主题只做一件事,就是给 html 加个 dark 类。

:root {
    --bg: #ffffff;  --text: #1a1a1a;  --text-light: #444444;
    --border: #d0d0d0;  --box-border: #b8b8b8;  --box-bg: #f7f7f7;
    --faint: #777777;
}
html.dark {
    --bg: #0c0c0c;  --text: #f0f0f0;  --text-light: #bbbbbb;
    --border: #2a2a2a;  --box-border: #3a3a3a;  --box-bg: #161616;
    --faint: #999999;
}

设计规范说起来就几条,单色灰阶,hover 只动 border-color,卡片 8px 圆角,不要阴影,字体用 LXGW。没什么花哨的,但每个页面看起来都像一家人。

早期做的跳转中间页不是这样。蓝色品牌色,渐变图标,大圆角,好几层阴影,跟主站放在一起像两拨人干的活儿。

旧版外链中间页
旧版,蓝色品牌色加渐变图标加药丸按钮

后来对着主站的变量重写了一遍。遮罩换成中性深灰,卡片回到 1px 边框加 8px 圆角,图标改成单色描边,按钮从那种 999px 的药丸形缩成 6px 圆角。颜色全部走变量,暗色模式自动跟着变,不用额外写一行覆盖代码。

新版外链中间页
新版,与主站同一套设计语言

页头还统一加了返回主页的小按钮,桌面端和手机上都没溢出。

五、外链跳转中间页

点站外链接不会直接跳走,先落到自己站上的 go/ 中间页。中间页上写着目标域名、完整地址(带个复制按钮)和一行提示,点了「继续前往」才真正跳走。页面底部固定着一栏来源信息,包括来源地址、站点名和页面标题,每次跳转都能追溯回去。

最早其实还多了一层确认弹窗,跟中间页的内容几乎一样,都是域名加警示语,等于打扰用户两遍。后来想通了,把弹窗删掉,点击外链直接落中间页,一圈确认只做一次。

几个实现细节值得单独说一说。拦截用的是事件委托,所以后面动态渲染出来的链接也一样会生效,给链接加个 data-outlink-ignore 就能豁免。URL 只放行 http/https,javascript: 这类协议直接拦掉,所有动态文本一律用 textContent 写入,防 XSS。target="_blank" 的链接会在新标签打开中间页,万一被浏览器拦截了就自动退回当前页跳转。

六、导航页的状态系统

精选状态的卡片带了金色边框和淡金背景,分组置顶。讲真,金色是全站唯一带颜色的地方,本来全站都是灰阶,这个纯粹是需求提了才破的例。

精选金色卡片
精选分组,金色卡片置顶显示

暂时失联的卡片会罩一层灰色半透明蒙版,内容还在但看着灰扑扑的,鼠标悬停时蒙版变淡能看清。卡片顶部有一条红条,上面写失联原因,这个字段只在后台选了「暂时失联」的时候才出现。没填原因的话就显示「未注明」。

失联卡片
暂时失联,洗灰卡片加顶部红条原因

还有个改动,描述文字不再截断成两行了。早期 CSS 写了个 -webkit-line-clamp: 2,长描述会被省略号硬生生切断,后来去掉了,现在多长的描述都能完整显示。

七、零数据库后台

没数据库,后台就是一个 PHP 文件加两个 JSON。

hyperos-login.php   后台本体(PHP)
sites.json        站点数据(名称/图标/描述/链接/状态/失联原因)
page.json         页面信息(标签标题/主标题/描述/副标题)
admin-config.php  密码哈希(删它即恢复默认密码)

写入策略用的是先写 .tmp 再 rename,就算写到一半断电也不会留下半个坏掉的 JSON。安全方面,入口文件名改过了(admin.php 变成 hyperos-login.php,不容易被猜到),登录态加 CSRF token,密码哈希单独放一个文件,删掉就恢复默认密码。前台那边如果 JSON 读取失败会静默回退到页面内嵌的数据,就算部署到纯静态空间也不会白屏。

后台功能大概有这么些。状态支持三态切换,已上线、精选、暂时失联,选失联才会弹出原因输入框。搜索是实时的,匹配名称、描述、链接和状态,读的是输入框当前的值,也就是说你改了但还没保存的内容也会参与匹配,隐藏的行不影响保存。列表可以按首字母分组,内置了 20892 个常用汉字的拼音映射,中文名也能正确归组,不想分组了取消勾选就恢复手动排序,那个顺序就是前台的展示顺序。添加网站用的是悬浮窗,填完点一下就自动存盘,不用再单独点保存按钮。

后台按首字母分组
后台,按首字母分组加失联原因列

添加网站悬浮窗
添加网站悬浮窗,失联原因仅在选「暂时失联」时出现

后台搜索
实时搜索,匹配计数加行内过滤

添加流程自动保存
填写,添加并保存,自动存盘,弹窗自动关闭

八、踩坑记录

1. 面板编辑器会把 JS 里的 HTML 实体解码。 通过主机面板的文件管理器直接改线上 index.html,保存后 ' 变成了裸的单引号,转义函数直接语法错误,整个脚本崩溃,前台数据全无。这事儿我中了两次。最后的解法是把标题和描述搬进后台的 page.json 管理,从此不再直接碰线上 HTML。教训很简单,面板编辑器不是代码编辑器,含转义字符的文件别用它改。

2. 别依赖 mbstring。 后台截断超长输入时用了 mb_strlen,本地 PHP 没启用这个扩展直接 500,虚拟主机大概率也没开。后来改用 PCRE 的 /./us 按 UTF-8 字符截断,零依赖。

3. [hidden] 打不过 class。 悬浮窗里写了 .am-label{display:block},优先级比浏览器默认的 [hidden]{display:none} 还高,导致失联原因字段在该藏的时候也露着脸。补了一条 .am-label[hidden]{display:none} 才摁下去。以后用 hidden 属性控显隐的话,记得显式补一条覆盖规则。

4. 无头 Chrome 的 virtual-time 陷阱。 --virtual-time-budget 遇到 JS 主动跨文档导航会死锁挂起,自动化测试全卡住。解法要么在解析期同步点击不用定时器,要么直接用 --dump-dom 验证落地页。

5. 截图会被 localStorage 污染。 同一个 Chrome profile 跑完暗色测试,后续截图全变暗色,差点误判成样式 bug。后来学乖了,亮暗两套截图必须分别用全新的 profile 跑。

九、小结

技术栈摊开了就这些,静态 HTML、原生 JS、一个 PHP 后台、两个 JSON。没框架没构建没数据库,一台最便宜的虚拟主机就能撑起来,后台够用好几年。

下一步准备把米表后台也接进这套流程(目前米表数据放在 domains.json 里,后台能力其实已经有了),再给全站补个 sitemap 和 RSS。


😊
提交

评论区

共 1 条评论

  1. 头像
    小彦 Lv1
    域名 forevernote.cn 不错耶,适合不会跑路的个人开发者
    3小时前 广东