
























TIP修改前必读:
- 本帖基于
Astro框架的 Fuwari 主题 进行修改方案编写,因此请读者优先掌握 Astro Docs 的内容后再来进行魔改。- 由于修改内容较多,以及可能会导致意料之外的事情,推荐使用
Github配合VSCode进行修改,方便随时备份恢复
之前一直想在侧边栏加一个访问统计的功能,看了不少教程发现大部分都是基于不蒜子或者 vercount 的,偶然间发现了通过 Umami + PHP 搭配实现的方式,觉得不错就搞了一下,最终效果就是在侧边栏展示了 当前在线、今日访客、今日浏览、昨日浏览、本月浏览、总浏览 这些数据,这篇文章就来分享一下怎么实现的。
本文参考了 梦爱吃鱼佬的文章 并在其基础上做了一些扩展,感谢原作者的分享!
侧边栏会多出一个统计区域,分为三行两列展示:
| 当前在线 | 今日访客 |
|---|---|
| 今日浏览 | 昨日浏览 |
| 本月浏览 | 总浏览 |
数据通过前端 JS 从 PHP 接口获取,每次页面加载时自动更新。
整个流程其实很简单:
为什么要加一层 PHP?因为 Umami 的 API 需要 Token 认证,直接在前端调用会暴露 Token,所以用 PHP 做一层代理。
以前获取 Token 需要用 Hoppscotch 手动调 API,比较麻烦。现在梦爱吃鱼佬做了一个在线工具,一键搞定。
打开 Umami API 生成工具,依次填入:
工具会自动登录获取 Token、拉取网站列表,完全不用手动调 API。确认数据无误后,点击 导出 API 文件,会下载一个 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,记得不要把这个文件的源码泄露出去了
在 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 路由切换后统计数据也能刷新
修改 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);
}
这样数字就会从当前值平滑过渡到目标值,视觉效果好很多。
umami-status.js 里的 API_URL 是否正确Umami 的 /active 接口返回的是最近 5 分钟内的活跃用户数,本身就是一个近似值,不用太纠结精确度。
很简单,你只需要:
Profile.astro 里加对应的 HTMLumami-status.js 里加对应的 set() 调用三步对应上就行,想加什么加什么。
整个实现其实不复杂,核心就是 Umami + PHP + 前端三件套,PHP 做中转解决了 Token 安全问题,前端 JS 负责渲染,改一改侧边栏组件就完事了。
如果你不想自己搭 Umami,也可以用别人公共的实例,但要注意数据隐私问题。自建的话推荐用 Docker 一键部署,配合宝塔管理很方便,可以参考我之前写的 宝塔部署教程(虽然那篇是讲 Twikoo 的,但 Docker 部署的思路是一样的)。
以上就是全部内容了,有问题评论区见!
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。