惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
人人都是产品经理
人人都是产品经理
Cisco Talos Blog
Cisco Talos Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
V
V2EX
博客园 - 三生石上(FineUI控件)
Martin Fowler
Martin Fowler
WordPress大学
WordPress大学
D
Docker
S
SegmentFault 最新的问题
博客园 - 聂微东
美团技术团队
Apple Machine Learning Research
Apple Machine Learning Research
月光博客
月光博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Last Week in AI
Last Week in AI
M
MIT News - Artificial intelligence
F
Fortinet All Blogs
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
The GitHub Blog
The GitHub Blog
GbyAI
GbyAI
L
LangChain Blog
Vercel News
Vercel News
博客园 - 叶小钗
MongoDB | Blog
MongoDB | Blog
Stack Overflow Blog
Stack Overflow Blog
H
Help Net Security
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
The Cloudflare Blog
Engineering at Meta
Engineering at Meta
T
Threat Research - Cisco Blogs
T
Threatpost
Scott Helme
Scott Helme
T
Tailwind CSS Blog
Latest news
Latest news
Stack Overflow Blog
Stack Overflow Blog
Blog — PlanetScale
Blog — PlanetScale
The Register - Security
The Register - Security
罗磊的独立博客
P
Proofpoint News Feed
腾讯CDC
S
Schneier on Security
雷峰网
雷峰网
A
About on SuperTechFans
T
Tenable Blog
F
Full Disclosure
Cyberwarzone
Cyberwarzone
博客园_首页
有赞技术团队
有赞技术团队
K
Kaspersky official blog

文章列表

为Fuwari框架适配友链状态显示 免费领取网易云音乐7天会员 小米MiMo Token Plan免费送辣! Astro框架Fuwari主题实现仿hexo-abbrlink功能
Astro框架Fuwari主题侧边栏添加Umami访问统计
辰渊尘 · 2026-05-09 · via
TIP

修改前必读:

  • 本帖基于 Astro框架Fuwari 主题 进行修改方案编写,因此请读者优先掌握 Astro Docs 的内容后再来进行魔改。
  • 由于修改内容较多,以及可能会导致意料之外的事情,推荐使用 Github 配合 VSCode 进行修改,方便随时备份恢复

前言#

之前一直想在侧边栏加一个访问统计的功能,看了不少教程发现大部分都是基于不蒜子或者 vercount 的,偶然间发现了通过 Umami + PHP 搭配实现的方式,觉得不错就搞了一下,最终效果就是在侧边栏展示了 当前在线、今日访客、今日浏览、昨日浏览、本月浏览、总浏览 这些数据,这篇文章就来分享一下怎么实现的。

本文参考了 梦爱吃鱼佬的文章 并在其基础上做了一些扩展,感谢原作者的分享!

效果预览#

侧边栏会多出一个统计区域,分为三行两列展示:

当前在线今日访客
今日浏览昨日浏览
本月浏览总浏览

数据通过前端 JS 从 PHP 接口获取,每次页面加载时自动更新。

需要的东西#

  • 一个已经部署好的 Umami 实例(自建或者用别人的都行)
  • 一个能跑 PHP 的 服务器(宝塔、虚拟主机之类的都行)
  • 你的博客是基于 Astro + Fuwari 主题

原理说明#

整个流程其实很简单:

  1. Umami 负责收集你博客的访问数据
  2. PHP 文件 作为中间层,调用 Umami 的 API 获取统计数据,然后以 JSON 格式返回给前端
  3. 前端 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 文件的访问地址,后面前端要用。

WARNING

PHP 文件里包含了你的 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 怎么办?#

  1. 检查 PHP 文件是否正常返回 JSON,直接访问 PHP 文件地址看看
  2. 检查 umami-status.js 里的 API_URL 是否正确
  3. 打开浏览器 F12 控制台看有没有报错
  4. 确认 Umami 确实在正常收集数据

在线人数不准确?#

Umami 的 /active 接口返回的是最近 5 分钟内的活跃用户数,本身就是一个近似值,不用太纠结精确度。

想自定义显示哪些统计项?#

很简单,你只需要:

  1. 在 PHP 文件里返回你想要的字段
  2. Profile.astro 里加对应的 HTML
  3. umami-status.js 里加对应的 set() 调用

三步对应上就行,想加什么加什么。

写在最后#

整个实现其实不复杂,核心就是 Umami + PHP + 前端三件套,PHP 做中转解决了 Token 安全问题,前端 JS 负责渲染,改一改侧边栏组件就完事了。

如果你不想自己搭 Umami,也可以用别人公共的实例,但要注意数据隐私问题。自建的话推荐用 Docker 一键部署,配合宝塔管理很方便,可以参考我之前写的 宝塔部署教程(虽然那篇是讲 Twikoo 的,但 Docker 部署的思路是一样的)。

以上就是全部内容了,有问题评论区见!

参考资料#