给博客页脚加了两行数字:静态站直连 Umami 的零后端玩法

1562 字
8 分钟
给博客页脚加了两行数字:静态站直连 Umami 的零后端玩法
给博客页脚加了两行数字:静态站直连 Umami 的零后端玩法

前两篇写的都是首页贴纸卡片和更新日志页,来源都是刷 rainzt.cn 刷出来的羡慕。这次又轮到它了——刷到页脚,发现最底下挂着两行东西:

rainzt.cn 页脚那行跳秒的运行时间
rainzt.cn 页脚那行跳秒的运行时间

一行是「本站已运行 82 天 23 小时 18 分 53 秒」,秒数还在跳;再往下一行是四个数字:今日访客、今日访问、本站访客、本站访问。

运行时间那行好办,纯前端算就行。真正让我停住的是第二行。

第一反应:静态站读不到吧#

我的博客是 Astro 构建出来的纯静态站,跑在 Vercel 上。Umami 统计是另外部署的一个实例,数据在它自己那边。

按常识,浏览器要从 www.9ll.uk 去读 umami.9ll.uk 的数据,跨域了,得对方点头才行。而我没法在 Vercel 上加任何服务端代码去代理这个请求——那就不叫静态站了。

所以本来是打算放弃的。后来想,反正 curl 一下也不费事,先看看接口长什么样:

Terminal window
curl -sS -i "https://umami.9ll.uk/api/share/你的分享ID"

响应头里明晃晃一行:

Access-Control-Allow-Origin: *

行吧,Umami 的分享接口本来就是设计给外部页面调用的,人家压根没打算拦。

顺带拿到了后面要用的两个东西:websiteId,还有一枚 JWT。

第二步卡住了:401#

有分享 ID 和 token 了,接下来取数据:

Terminal window
curl -sS -H "x-umami-share-token: $TOKEN" \
"https://umami.9ll.uk/api/websites/$WEBSITE_ID/stats?startAt=0&endAt=$NOW"

返回:

{"error":{"message":"Unauthorized","code":"unauthorized","status":401}}

token 是从接口现取的,长度 619,没问题。那就只能是我传的方式不对。试了塞进 query 参数 ?token=,401;试了 Authorization: Bearer,还是 401。

去翻参考实现才看到,真正要传的是两个头:

x-umami-share-token: <JWT>
x-umami-share-context: 1

少一个就是 401。补上第二个,数据立刻回来了:

{"pageviews":4741,"visitors":680,"visits":1025,"bounces":532,"totaltime":265264}

startAt=0 表示全站,把它换成今天零点的时间戳,拿到的就是今日数据。四个指标正好对应页脚要的四项:

页脚显示来源
今日访客今日 visitors
今日访问今日 pageviews
本站访客全站 visitors
本站访问全站 pageviews

代码放哪:还是原生注入点#

落点不用纠结,前两篇已经踩过路了——src/config/FooterConfig.html。

这是 Firefly 自带的页脚 HTML 注入点,Footer.astro 在构建时用 fs 把这个文件读出来,再 set:html 塞进页脚。这个文件在 src/config/ 目录下,本来就是留给用户填自定义内容的,改它不算动主题源码,以后合并上游更新也不会冲突。

一个容易踩的细节

set:html 注入的内容不经过 Astro 的处理,所以里面写 <style> 和 <script> 会原样输出、照常生效,不需要额外包装。反过来,Footer.astro 里有一句正则会把 HTML 注释 <!-- --> 整段删掉,所以别在这个文件里用 HTML 注释写正经内容。

另外一件事让我省了不少心:<Footer /> 是挂在 #swup-container 外面的。这意味着开着 Swup 的页面切换不会重新渲染页脚,注入的脚本只在整页加载时跑一次,不用像参考实现那样写一堆防重入判断。

实现里的几个决定#

整段逻辑就是一个 IIFE,塞在注入的 <script> 里,没有新增任何文件。

缓存分三层。分享令牌 24 小时、今日数据 60 秒、全站数据 5 分钟,都落在 localStorage。令牌有效期本来就长,没必要每次刷新都去换;今日数据变得快,60 秒是个折中;全站那几个数字半天不一定动一下,5 分钟足够。

请求挂在 window.load 之后的 requestIdleCallback 上。页脚统计再怎么说也是装饰,没道理让它跟首屏抢带宽。

页面停留时每 90 秒静默刷新一次。本来第一版是拉一次就不管了,但想想访客在文章页读个十分钟,数字一直不动也挺奇怪的。加了轮询之后还有个副作用:document.hidden 为真时直接跳过,切到别的标签页一次请求都不发。

拉取失败就整行藏起来。不显示 0,也不显示「加载中」——页脚里留半截残骸比什么都没有更难看。控制台留一条 warn 方便排查就够了。

数字是滚上去的,从 0 滚到目标值,900ms 三次方缓动。系统开了「减少动态效果」的话直接落值,不折腾。

跳秒那行:时区写死是有意为之#

运行时间比统计简单得多,但有个细节值得说。

建站日期我直接取了 siteConfig.siteStartDate 里的值(2026-03-24)。真正要注意的是怎么解析它:

new Date("2026-03-24") // ❌ 按 UTC 午夜解析,东八区变成当天早上 8 点
new Date("2026-03-24T00:00:00+08:00") // ✅ 明确东八区

第一种写法在我的机器上看不出问题,但访客要是在别的时区,算出来的天数就会差一天。把时区写死,不管谁访问都是同一个数字。

至于「切后台就停表」——setInterval 每秒跑一次本身不重,但没道理让它在看不见的地方一直空转。切回去的时候立刻重算一次,也不会出现停表期间时间对不上的情况。

成品#

跑起来之后长这样:

页脚最终效果:运行时间在上,四个统计数字在下
页脚最终效果:运行时间在上,四个统计数字在下

红色那条是主题色,跟着站点配色走,暗色模式下会自己换。数字用了 tabular-nums,不然滚动的秒数会让整行宽度一直在抖。

顺手把这次改动也记进了更新日志(V1.11)。

说点代价#

这套东西确实不需要后端,但它换来的是分享链接是公开的。分享 ID 会明文出现在 HTML 里,任何人拿到都能读到你站点的访问量——这本来就是 Umami 分享功能的用途。介意的话去后台把分享删掉就行,页脚那行会自己隐藏。

另一个是它只读数据、不写数据,所以不必担心开发环境访问页面会污染统计。本地 pnpm dev 打开也能正常看到数字,调试反而更方便了。

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!

打赏
给博客页脚加了两行数字:静态站直连 Umami 的零后端玩法
https://www.9ll.uk/posts/blog/footerstats/
作者
HY
发布于
2026-09-28
许可协议
CC BY-NC-SA 4.0
相关文章智能推荐
1
给博客加了张站点统计看板:自研翻车之后直接抄了整份实现
博客看上 rainzt.cn 的 /analytics/ 之后,我先照着自己设计了一版——能跑,但完全不是那个味道。被一句「你不会整就去抄人家代码」点醒,去翻同源 fork 的仓库才发现:人家根本不是单个文件,页面将近四千行,外加两个组件,少一个的话页面其余模块全正常、只有四个数字永远停在「加载中」。搬完又改了四轮:整页蓝换成主题色、推送数从 1695 修正到 252、热力图改 GitHub 绿、角标不再压住插画。
2
给博客加了张更新日志页
博客羡慕 rainzt.cn 的博客日志页,把它的页面结构扒下来,用 Firefly 自己的组件体系重做了一遍。全程只新增三个文件,不碰主题源码,以后合并上游更新不受影响。顺便把折叠动画从 max-height 换成了 grid-template-rows。
3
给博客加了个 Steam 游戏库:没后端,蹭朋友圈的定时任务喂数据
博客参考站用 Halo 插件在服务端拉 Steam Web API,我这静态站没后端,改成跟朋友圈同一套定时快照,顺带补了没 Key 时的降级路径。
4
给友链页加了悬停预览:12 张截图离线截好,主题文件一行没动
博客在 blog.amamo.top 的友链页看到鼠标悬停弹首页截图的效果。我这边的约束是不能动主题源文件,所以换了个做法:截图用本机浏览器离线截、转 webp 存进仓库,卡片用一段独立脚本在浏览器里现场拼。12 张封面一共 190 KB,悬停之前一张都不加载。
5
给博客首页加了张可拖拽贴纸卡片:一次合并友好的魔改
博客看到 rainzt.cn 的首页毛玻璃卡片和能拖动的贴纸羡慕了,扒下来装到自己的 Firefly 上。全程只新增文件不动主题源码,以后还能正常合并上游更新,顺便踩了一堆 CSS 的坑。后来又按性能数据把入场动画拆成两段、关掉了 Swup 视口预取;最后一轮重做了贴纸排布,桌面并成一排、手机收成一行,还加了张动图,并把贴纸改成延迟加载——手机端从 149 KB 降到 47 KB。最近又给卡片加了「开场两秒后自动收起」、右下角的贴纸开关,并让底部那排改成随机抽 10 张、左右各 5 张分列,把正中央让给下滑箭头。
随机文章随机推荐

评论区

Profile Image of the Author
HY
Hello, I'm HY.
公告
迷茫.... 为了访问速度,本站将会在11月上旬至中旬迁移到0dm.cn并完成备案
分类
标签
最新动态
站点统计
文章
74
分类
8
标签
192
总字数
161,979
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
Vercel
博客版本
Firefly v6.16.8
文章许可
CC BY-NC-SA 4.0
文章目录