搜索 K
主题
@vaultkit/vitepress-plugin-umami 为 VitePress 注入隐私优先的 Umami 统计脚本。它不是运行时组件,而是一个纯构建期函数:把 Umami 配置转换成一条 head 内联脚本条目,塞进 VitePress 的 head 数组即可。
内联脚本先做两道豁免检查——尊重浏览器 Do Not Track 信号、跳过 localhost/127.0.0.1 本地访问——检查通过才以 defer 方式挂载真实跟踪脚本。整个包零运行时依赖,产物只是一段字符串。
想知道「哪篇文章有人读」不该以读者隐私为代价。Umami 是开源、无 Cookie、匿名聚合的轻量统计(脚本 2KB 量级),本身已经比 GA 干净得多;但直接抄官方的 <script> 接入片段,还是会踩三个坑:
vitepress dev / vitepress preview 的每次刷新都计入线上统计<script src> 不带 defer 会阻塞解析这三个问题的处理逻辑每个站点都要重写一遍,本包把它固化成一个函数调用。
pnpm add -D @vaultkit/vitepress-plugin-umami使用 @vaultkit/vitepress-theme-vault 主题则开箱即带:site 配置里 features.analytics 传 provider: 'umami' 判别式对象即可(缺省不开启,统计是显式 opt-in):
export default defineConfigWithTheme({
extends: createVaultTheme({
site: { /* ... */ },
features: {
analytics: {
provider: 'umami',
src: 'https://cloud.umami.is/script.js',
websiteId: 'xxxx-xxxx-xxxx',
},
},
}),
})裸 VitePress 场景,.vitepress/config.ts 的 head 注入:
import { umamiHead } from '@vaultkit/vitepress-plugin-umami'
import { defineConfig } from 'vitepress'
export default defineConfig({
head: [
umamiHead({
src: 'https://cloud.umami.is/script.js', // 或自托管地址
websiteId: 'xxxx-xxxx-xxxx',
}),
],
})websiteId 在 Umami 后台「Settings → Websites → Edit」里能找到。部署上线后(注意本地预览被豁免,看不到数据)访问几次,后台实时面板就该有记录了。
| 导出 | 形态 | 说明 |
|---|---|---|
umamiHead | 函数 | (options: UmamiOptions) => UmamiHeadEntry,生成 head 条目 |
UmamiOptions | 类型 | 选项接口 |
UmamiHeadEntry | 类型 | [string, Record<string, string>, string]——VitePress HeadConfig 的结构化子集,免除对 vitepress 的类型依赖 |
stringhttps://cloud.umami.is/script.js;自托管实例填自己的地址(如 https://analytics.example.com/script.js)。Umami 没有「缺省官方地址」——它以自托管为第一公民,所以本字段必填而非带默认值(对比 Plausible 包的 src 有缺省,见文末姊妹篇)。stringdata-website-id 属性。一个 Umami 实例可管多个站点,靠它区分归属。Record<string, string>,默认 undefined<script> 属性透传,官方脚本的可选 data-* 配置全部经此注入。常用项:umamiHead({
src: 'https://cloud.umami.is/script.js',
websiteId: 'xxxx-xxxx-xxxx',
attrs: {
'data-domains': 'example.com', // 只在指定域名上报(防止镜像站/预览环境计入)
'data-host-url': 'https://analytics.example.com', // 上报端点与脚本托管分离时
'data-auto-track': 'false', // 关自动跟踪、改手动埋点
},
})同名键会覆盖内置的 data-website-id(展开顺序上 attrs 在后),一般不要这么做。
umamiHead(...) 返回的条目形如 ['script', {}, bootstrap],最终渲染进每页 HTML 的是一段自执行内联脚本(此处为可读性加了换行,实际是单行):
<script>
(function () {
if ((navigator.doNotTrack || window.doNotTrack) === '1'
|| location.hostname === 'localhost'
|| location.hostname === '127.0.0.1')
return
var s = document.createElement('script')
s.defer = true
s.src = 'https://cloud.umami.is/script.js'
s.setAttribute('data-website-id', 'xxxx-xxxx-xxxx')
document.head.appendChild(s)
})()
</script>豁免命中时函数直接返回,页面上不存在任何跟踪脚本节点——不是「加载了但不上报」,而是压根不加载。
navigator.doNotTrack(部分旧浏览器挂在 window.doNotTrack)为 '1' 即用户显式开启「请勿跟踪」。这个信号在行业里长期被无视,但对隐私优先的统计选型来说,尊重它是姿态的自然延伸:连匿名统计都不加载,用户的表态被完整执行。
location.hostname 为 localhost 或 127.0.0.1 时直接返回。VitePress 的 head 注入是构建期的静态行为,dev/preview 与生产共用同一份配置——没有这道豁免,本地开发的每次热更新刷新都会污染线上数据。放在客户端判断(而非构建期条件注入)意味着同一份产物部署到任何环境都行为正确。
真实脚本以 s.defer = true 动态创建,浏览器在 HTML 解析完成后才执行它,首屏渲染路径零阻塞。统计脚本永远不该出现在关键渲染路径上。
另外值得一提:Umami 官方脚本自带 SPA 路由跟踪(监听 History API),VitePress 客户端路由切换会自动计为页面浏览,无需任何额外埋点。
两个姊妹包的注入机制完全一致,选型看服务本身:
| 维度 | Umami | Plausible |
|---|---|---|
| 自托管成本 | 低(Node + Postgres/MySQL,Docker 一键) | 中(Elixir + Postgres + ClickHouse) |
| 云服务免费额度 | Cloud 有免费档 | 仅付费(30 天试用) |
| 配置差异 | src 必填 + websiteId | domain 必填,src 有官方缺省 |
| 多站点管理 | 一实例多站点,按 websiteId 区分 | 按 domain 区分 |
倾向自托管、想要免费方案选 Umami;愿意付费换取托管省心、或团队已有 Plausible 选后者。两者都无 Cookie、GDPR 友好、脚本 2KB 量级,数据维度也大同小异——博客场景闭眼选任何一个都不会错。
Q:本地怎么都看不到统计请求? 这是 localhost 豁免在工作,属预期行为。想本地验证可以用 127.0.0.1 以外的 hosts 别名访问,或直接部署到预览环境。
Q:自己浏览器里看不到脚本加载? 先检查是否开了浏览器的 DNT 设置(或隐私插件注入了 DNT)——命中豁免时脚本节点根本不会创建。
Q:从 Umami Cloud 迁到自托管要改什么? 只改 src 指向自己的实例地址;websiteId 在新实例里重建站点后更新。上报端点与脚本分离部署时配 attrs['data-host-url']。
Q:镜像站/多域名部署导致数据混入? 配 attrs['data-domains'] 白名单,只有列出的域名会上报。