前言
我的博客一直托管在 Cloudflare Pages 上,借着 “赛博菩萨” 的免费额度,过得还算滋润。之前写过一篇用 HanAnalytics 做网站统计的文章(那篇在这儿),那个方案说白了是给我自己看的——想看数据还得登录后台一层层点开,访客是无感的。
但作为一个有点强迫症的站长,我总觉得少了点东西:来访的你,能不能在页面上直接看到这篇文章被读过多少次?全站又一共被访问了多少次?这种 “热腾腾” 的访问量数字,才是博客的烟火气嘛。
于是趁着周末有空,我给小站接上了一套站内访问量统计,顺手还把积压已久的依赖漏洞修了一波。下面聊聊思路和踩坑。
一、为什么不直接用现成统计
你可能会说:装个 Google Analytics、或者我之前用的 HanAnalytics 不就行了?
确实能统计,但它们有两个让我不太舒服的点:
- 访客看不到:数据是站长的私产,访客在页面上什么都看不到,少了点互动感;
- 依赖第三方:要么喂巨头数据,要么再起一个服务,违背了我 “能不花钱、能不麻烦就不麻烦” 的装机原则。
而我这博客本就跑在 Cloudflare Pages 上,它自带 Pages Functions(边缘无服务器函数)和 KV(键值存储),全都在免费额度内。把访问量存在自己的 KV 里、用 Pages Functions 做接口,数据完全自控、零额外服务——这不就是 “赛博菩萨” 的正确打开方式吗?
二、方案:Pages Functions + KV
思路很朴素:
- 在
functions/api/views.ts里写一个 Serverless 函数,接收路径参数; - 用 KV 命名空间当计数器,POST 就自增,GET 就读取;
- 前端每个页面加载时调一下接口,把数字渲染出来。
为了避免重复计数,我是这么设计的:
- 全站访问量:每次页面访问
+1,存到 KV 的固定 key(比如__site__); - 每篇文章访问量:文章页额外按当前路径
location.pathname自增一次,存成各自独立的 key。
这样一来,站点总数是所有页面的真实访问量,文章数各自独立,互不打架。
三、前端怎么接
本站用了 Swup 做无刷新页面切换,所以计数器不能只在首屏跑一次——切到另一篇文章时也得更新。我写了个小组件 ViewCounter:
- 初始加载时拉一次数据;
- 监听 Swup 的
page:view事件,页面切换后再拉一次。
于是文章页头部会显示 “阅读量 N”,页脚常驻 “全站访问量 N”。数字加载前先显示 —,拿到接口返回再填上,体验还算顺滑。
NOTE按我的习惯,这里统计的是 每次加载都
+1(包括刷新)。简单直观,虽然刷新会虚高一点点,但对一个个人博客来说无伤大雅。
四、顺手修了一波依赖漏洞
借着折腾的机会,我顺手做了一次依赖大保健。一跑 pnpm audit,好家伙——168 个漏洞,3 个 critical、79 个 high,吓得我手里的枸杞茶都洒了。
按 package.json 里的版本范围把直接依赖更新到最新兼容版,再用 pnpm.overrides 强制修掉几个麻烦的传递依赖:
serialize-javascript(构建链里的 RCE)brace-expansion(glob/minimatch 链的 DoS)sharp(libvips 相关)
一番操作下来,168 → 17。剩下的 17 个,全部来自 astro 自身——官方公告里写着修复版本是 astro >= 7.0.4,也就是说得做大版本破坏性升级才能清零。
我去翻了翻上游 saicaca/fuwari,发现官方主线也还停留在 astro 5.x,同样带着这 17 个漏洞;他们连 Dependabot 都设成了 “忽略所有大版本更新”,显然也是在等一个合适的时机再大改。既然上游都没急,我这小破站也就先停在这里,等哪天有空再跟着上游的 Tailwind v4 迁移一起把 astro 升上去。
五、部署注意点
代码推上 GitHub 后,Cloudflare Pages 会自动构建。但要让计数器真正跑起来,还有一个必做动作:
在 Pages 项目 设置 → Functions → KV namespace bindings 里,把新建的 KV 命名空间绑定上去,变量名必须叫 VIEWS(和代码里的 env.VIEWS 对应),然后重新部署一次。
没绑定的话,/api/views 会因为找不到 KV 而报错,计数器就会一直停在 —。
结语
一套站内访问量统计,加上一次依赖大保健,小站又健康了一点。现在你能在每篇文章底下看到它被读过多少次,也能在页脚看到全站的热度——如果你愿意,多点几下刷新,帮我把数字刷好看点(不是)。
下次再聊,可能是 astro 7 的迁移实录了。在那之前,欢迎常来逛逛,你的每一次访问,KV 都记着呢。