TIP修改前必读:
- 本帖基于
Astro框架的 Fuwari 主题 进行修改方案编写,因此请读者优先掌握 Astro Docs 的内容后再来进行魔改。- 由于修改内容较多,以及可能会导致意料之外的事情,推荐使用
Github配合VSCode进行修改,方便随时备份恢复
前言
之前一直想在侧边栏加一个访问统计的功能,看了不少教程发现大部分都是基于不蒜子或者 vercount 的,偶然间发现了通过 Umami + PHP 搭配实现的方式,觉得不错就搞了一下,最终效果就是在侧边栏展示了 当前在线、今日访客、今日浏览、昨日浏览、本月浏览、总浏览 这些数据,这篇文章就来分享一下怎么实现的。
本文参考了 梦爱吃鱼佬的文章 并在其基础上做了一些扩展,感谢原作者的分享!
效果预览
侧边栏会多出一个统计区域,分为三行两列展示:
| 当前在线 | 今日访客 |
|---|---|
| 今日浏览 | 昨日浏览 |
| 本月浏览 | 总浏览 |
数据通过前端 JS 从 PHP 接口获取,每次页面加载时自动更新。
需要的东西
- 一个已经部署好的 Umami 实例(自建或者用别人的都行)
- 一个能跑 PHP 的 服务器(宝塔、虚拟主机之类的都行)
- 你的博客是基于 Astro + Fuwari 主题 的
原理说明
整个流程其实很简单:
- Umami 负责收集你博客的访问数据
- PHP 文件 作为中间层,调用 Umami 的 API 获取统计数据,然后以 JSON 格式返回给前端
- 前端 JS 请求 PHP 文件的地址,拿到数据后渲染到侧边栏
为什么要加一层 PHP?因为 Umami 的 API 需要 Token 认证,直接在前端调用会暴露 Token,所以用 PHP 做一层代理。
步骤一:获取 Umami API 文件
以前获取 Token 需要用 Hoppscotch 手动调 API,比较麻烦。现在梦爱吃鱼佬做了一个在线工具,一键搞定。
打开 Umami API 生成工具,依次填入:
- 你的 Umami 服务地址
- 你的 Umami 账号和密码
- 选择你要统计的 网站
工具会自动登录获取 Token、拉取网站列表,完全不用手动调 API。确认数据无误后,点击 导出 API 文件,会下载一个 PHP 文件。
这个工具不会保存任何数据到远程服务器,所有信息仅存储在你的浏览器本地,可以放心使用
步骤二:部署 PHP 文件
把下载的 PHP 文件上传到你的服务器,确保能通过域名访问到就行。如果你用的是宝塔,直接丢到网站根目录即可。
访问这个 PHP 文件的地址,如果返回类似这样的 JSON 就说明成功了:
{ "today_uv": 42, "today_pv": 121, "online_users": 0, "yesterday_uv": 68, "yesterday_pv": 203, "last_month_pv": 7429, "last_year_pv": 19553, "total_uv": 3652, "total_pv": 19553}到这里,后端部分就搞定了,记下这个 PHP 文件的访问地址,后面前端要用。
WARNINGPHP 文件里包含了你的 Umami Token,记得不要把这个文件的源码泄露出去了
步骤三:添加前端代码
创建 JS 文件
在 public/js/ 目录下创建 umami-status.js:
const API_URL = "https://你的php文件地址/umami-api.php";
async function loadUmamiStats() { try { const res = await fetch(API_URL, { cache: "no-store" }); const data = await res.json();
function set(id, value) { const el = document.getElementById(id); if (el) el.textContent = value ?? 0; }
set("stat-online", data.online_users); set("stat-uv", data.today_uv); set("stat-today-pv", data.today_pv); set("stat-yesterday-pv", data.yesterday_pv); set("stat-month-pv", data.last_month_pv ?? 0); set("stat-total-pv", data.total_pv); } catch (err) { console.error("Umami API Error:", err); }}
/* 首次加载 */document.addEventListener("DOMContentLoaded", loadUmamiStats);
/* Astro 页面切换后重新执行 */document.addEventListener("astro:page-load", loadUmamiStats);cache: "no-store" 确保每次都获取最新数据,不会被浏览器缓存。astro:page-load 事件是 Fuwari 主题用的 Swup 页面过渡库提供的,确保 SPA 路由切换后统计数据也能刷新
引入 JS 文件
修改 src/layouts/Layout.astro,在 <head> 中加入:
<script type="module" src="/js/umami-status.js"></script>修改侧边栏组件
修改 src/components/widget/Profile.astro,在社交链接区域(</div> 闭合 flex-wrap 那个 div)后面添加统计区域:
<!-- 全站访问统计 --> <div class="grid grid-cols-2 mt-3 pt-3 border-t border-neutral-300 dark:border-neutral-700 gap-y-3"> <div class="text-center"> <div class="text-xs text-neutral-500 mb-1 flex items-center justify-center gap-1"> <span class="text-xs">当前在线</span> </div> <div id="stat-online" class="font-bold text-lg text-neutral-700 dark:text-neutral-300 umami-soft"> <span>0</span> </div> </div> <div class="text-center border-l border-neutral-300 dark:border-neutral-700"> <div class="text-xs text-neutral-500 mb-1 flex items-center justify-center gap-1"> <span class="text-xs">今日访客</span> </div> <div id="stat-uv" class="font-bold text-lg text-neutral-700 dark:text-neutral-300 umami-soft"> <span>0</span> </div> </div> </div> <div class="grid grid-cols-2 mt-3 pt-3 border-t border-neutral-300 dark:border-neutral-700 gap-y-3"> <div class="text-center border-neutral-300 dark:border-neutral-700 "> <div class="text-xs text-neutral-500 mb-1 flex items-center justify-center gap-1"> <span class="text-xs">今日浏览</span> </div> <div id="stat-today-pv" class="font-bold text-lg text-neutral-700 dark:text-neutral-300 umami-soft"> <span>0</span> </div> </div> <div class="text-center border-l border-neutral-300 dark:border-neutral-700"> <div class="text-xs text-neutral-500 mb-1 flex items-center justify-center gap-1"> <span class="text-xs">昨日浏览</span> </div> <div id="stat-yesterday-pv" class="font-bold text-lg text-neutral-700 dark:text-neutral-300 umami-soft"> <span>0</span> </div> </div> </div> <div class="grid grid-cols-2 mt-3 pt-3 border-t border-neutral-300 dark:border-neutral-700 gap-y-3"> <div class="text-center border-neutral-300 dark:border-neutral-700"> <div class="text-xs text-neutral-500 mb-1 flex items-center justify-center gap-1"> <span class="text-xs">本月浏览</span> </div> <div id="stat-month-pv" class="font-bold text-lg text-neutral-700 dark:text-neutral-300 umami-soft"> <span>0</span> </div> </div> <div class="text-center border-l border-neutral-300 dark:border-neutral-700"> <div class="text-xs text-neutral-500 mb-1 flex items-center justify-center gap-1"> <span class="text-xs">总浏览</span> </div> <div id="stat-total-pv" class="font-bold text-lg text-neutral-700 dark:text-neutral-300 umami-soft"> <span>0</span> </div> </div> </div>这段代码的结构是三个 grid-cols-2 的 grid 容器,每个容器放两个统计项,用 border-t 做分隔线。id 和 JS 里的 set() 函数对应,数据加载完成后会自动替换掉默认的 0。
进阶:加上数字滚动动画
如果你觉得数字直接跳变太生硬,可以加一个简单的数字滚动效果。
在 src/styles/main.css(或者你喜欢的地方)加上:
.umami-soft span { display: inline-block; transition: all 0.3s ease;}然后把 JS 里的 set 函数改成带动画的版本:
function set(id, value) { const el = document.getElementById(id); if (!el) return; const span = el.querySelector('span'); if (!span) return; const target = parseInt(value) || 0; const current = parseInt(span.textContent) || 0; if (current === target) return;
const duration = 600; const startTime = performance.now();
function animate(currentTime) { const elapsed = currentTime - startTime; const progress = Math.min(elapsed / duration, 1); // easeOutCubic const eased = 1 - Math.pow(1 - progress, 3); const currentVal = Math.round(current + (target - current) * eased); span.textContent = currentVal; if (progress < 1) { requestAnimationFrame(animate); } } requestAnimationFrame(animate);}这样数字就会从当前值平滑过渡到目标值,视觉效果好很多。
常见问题
数据全是 0 怎么办?
- 检查 PHP 文件是否正常返回 JSON,直接访问 PHP 文件地址看看
- 检查
umami-status.js里的API_URL是否正确 - 打开浏览器 F12 控制台看有没有报错
- 确认 Umami 确实在正常收集数据
在线人数不准确?
Umami 的 /active 接口返回的是最近 5 分钟内的活跃用户数,本身就是一个近似值,不用太纠结精确度。
想自定义显示哪些统计项?
很简单,你只需要:
- 在 PHP 文件里返回你想要的字段
- 在
Profile.astro里加对应的 HTML - 在
umami-status.js里加对应的set()调用
三步对应上就行,想加什么加什么。
写在最后
整个实现其实不复杂,核心就是 Umami + PHP + 前端三件套,PHP 做中转解决了 Token 安全问题,前端 JS 负责渲染,改一改侧边栏组件就完事了。
如果你不想自己搭 Umami,也可以用别人公共的实例,但要注意数据隐私问题。自建的话推荐用 Docker 一键部署,配合宝塔管理很方便,可以参考我之前写的 宝塔部署教程(虽然那篇是讲 Twikoo 的,但 Docker 部署的思路是一样的)。
以上就是全部内容了,有问题评论区见!
参考资料
文章修订历史 (1 次)
查看变更记录
feat: add git log and blog.bsgun.cn to allowed commands and domains