数据统计
为你的 VitePress 站点选择合适的访问统计方案,了解用户行为和内容表现。
方案对比
| 特性 | Google Analytics | 百度统计 | Cloudflare | Umami | Plausible | Microsoft Clarity |
|---|---|---|---|---|---|---|
| 类型 | 云服务 | 云服务 | 云服务 | 自托管/云 | 自托管/云 | 云服务 |
| 免费 | ✅ | ✅ | ✅ | ✅ 开源 | ⚠️ 付费 | ✅ |
| 国内访问 | ⚠️ 较慢 | ✅ 快速 | ✅ 快速 | ✅ 快速 | ⚠️ 较慢 | ⚠️ 较慢 |
| 隐私友好 | ⚠️ 一般 | ⚠️ 一般 | ✅ 良好 | ✅ 优秀 | ✅ 优秀 | ✅ 良好 |
| 热力图 | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
| 会话录制 | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
| 实时统计 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| 功能丰富度 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ |
| GDPR 合规 | ⚠️ 需配置 | ❌ | ✅ | ✅ | ✅ | ✅ |
选择建议
| 场景 | 推荐 | 原因 |
|---|---|---|
| 国际站点 | Google Analytics | 功能全面、行业标准 |
| 国内站点 | 百度统计 | 访问快速、SEO 加分 |
| 隐私优先 | Umami / Plausible | 无 Cookie、符合 GDPR |
| 免费功能 | Cloudflare | 完全免费、无限量 |
| 用户行为分析 | Microsoft Clarity | 免费热力图、会话录制 |
| 开源爱好者 | Umami | 完全开源可自托管 |
| 企业级 | Google Analytics + Clarity | GA 看数据,Clarity 看行为 |
Google Analytics
最流行的网站分析工具,功能全面,适合需要深入分析的站点。
基本配置
ts
// .vitepress/config.mts
export default defineConfig({
head: [
['script', { async: '', src: 'https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX' }],
['script', {}, `window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}gtag('js',new Date());gtag('config','G-XXXXXXXXXX');`]
]
})SPA 页面追踪
VitePress 是 SPA 应用,需要在路由切换时手动发送页面浏览事件:
ts
// .vitepress/theme/index.ts
import DefaultTheme from 'vitepress/theme'
import { onMounted } from 'vue'
import { useRouter } from 'vitepress'
export default {
extends: DefaultTheme,
setup() {
if (typeof window !== 'undefined') {
const router = useRouter()
onMounted(() => {
// 监听路由变化,发送页面浏览数据
router.onAfterRouteChanged = (to) => {
// @ts-expect-error gtag 全局变量
if (typeof gtag !== 'undefined') {
// @ts-expect-error gtag 全局变量
gtag('config', 'G-XXXXXXXXXX', {
page_path: to,
page_title: document.title
})
}
}
})
}
}
}事件追踪
追踪用户在文档中的关键行为:
ts
// .vitepress/theme/composables/useAnalytics.ts
import { onMounted } from 'vue'
import { useData } from 'vitepress'
export function useAnalytics() {
const { page } = useData()
function trackEvent(eventName: string, params?: Record<string, string>) {
if (typeof window !== 'undefined' && typeof gtag !== 'undefined') {
// @ts-expect-error gtag 全局变量
gtag('event', eventName, params)
}
}
// 追踪代码复制
function trackCodeCopy(language: string) {
trackEvent('code_copy', { language })
}
// 追踪搜索
function trackSearch(query: string) {
trackEvent('search', { search_term: query })
}
// 追踪外部链接点击
function trackOutboundLink(url: string) {
trackEvent('outbound_click', { url })
}
return { trackEvent, trackCodeCopy, trackSearch, trackOutboundLink }
}Cookie 同意管理
GDPR 要求在用户同意前不加载分析脚本:
vue
<!-- .vitepress/theme/components/CookieConsent.vue -->
<script setup lang="ts">
import { ref, onMounted } from 'vue'
const showBanner = ref(false)
onMounted(() => {
const consent = localStorage.getItem('cookie-consent')
if (!consent) {
showBanner.value = true
} else if (consent === 'accepted') {
loadAnalytics()
}
})
function accept() {
localStorage.setItem('cookie-consent', 'accepted')
showBanner.value = false
loadAnalytics()
}
function decline() {
localStorage.setItem('cookie-consent', 'declined')
showBanner.value = false
}
function loadAnalytics() {
const script1 = document.createElement('script')
script1.async = true
script1.src = 'https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX'
document.head.appendChild(script1)
const script2 = document.createElement('script')
script2.textContent = `window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}gtag('js',new Date());gtag('config','G-XXXXXXXXXX',{anonymize_ip:true});`
document.head.appendChild(script2)
}
</script>
<template>
<div v-if="showBanner" class="cookie-consent">
<p>本站使用 Cookie 进行数据分析,以改善用户体验。</p>
<div class="cookie-actions">
<button class="accept" @click="accept">同意</button>
<button class="decline" @click="decline">拒绝</button>
</div>
</div>
</template>
<style scoped>
.cookie-consent {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: var(--vp-c-bg-soft);
border-top: 1px solid var(--vp-c-divider);
padding: 16px 24px;
display: flex;
align-items: center;
justify-content: space-between;
z-index: 999;
}
.cookie-actions {
display: flex;
gap: 8px;
}
.accept, .decline {
padding: 6px 16px;
border-radius: 6px;
border: none;
cursor: pointer;
}
.accept {
background: var(--vp-c-brand);
color: white;
}
.decline {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
}
</style>百度统计
国内访问速度最快的统计方案,对 SEO 有加分效果。
基本配置
ts
// .vitepress/config.mts
export default defineConfig({
head: [
['script', {}, `var _hmt=_hmt||[];(function(){var hm=document.createElement("script");hm.src="https://hm.baidu.com/hm.js?YOUR_SITE_ID";var s=document.getElementsByTagName("script")[0];s.parentNode.insertBefore(hm,s);})();`]
]
})SPA 页面追踪
ts
// .vitepress/theme/index.ts
import DefaultTheme from 'vitepress/theme'
import { onMounted } from 'vue'
import { useRouter } from 'vitepress'
export default {
extends: DefaultTheme,
setup() {
if (typeof window !== 'undefined') {
const router = useRouter()
onMounted(() => {
router.onAfterRouteChanged = (to) => {
// @ts-expect-error _hmt 全局变量
if (window._hmt) {
// @ts-expect-error _hmt 全局变量
window._hmt.push(['_trackPageview', to])
}
}
})
}
}
}Cloudflare Analytics
Cloudflare 提供的免费 Web Analytics,无需 Cookie,隐私友好。
基本配置
ts
// .vitepress/config.mts
export default defineConfig({
head: [
['script', { defer: '', src: 'https://static.cloudflareinsights.com/beacon.min.js', 'data-cf-beacon': '{"token":"YOUR_TOKEN"}' }]
]
})特点
- 完全免费,无限量页面浏览
- 无需 Cookie,不侵犯用户隐私
- 国内访问快速
- 自动追踪 SPA 路由切换,无需额外配置
Umami
开源、自托管的网站分析工具,界面简洁,隐私友好。
Docker 自托管
bash
docker run -d --name umami \
-p 3000:3000 \
-e DATABASE_URL=postgresql://umami:umami@db:5432/umami \
-e APP_SECRET=your-app-secret \
ghcr.io/umami-software/umami:postgresql-latestVercel 一键部署
- Fork Umami 仓库
- 在 Vercel 导入并部署
- 配置数据库环境变量
基本配置
ts
// .vitepress/config.mts
export default defineConfig({
head: [
['script', { defer: '', src: 'https://your-umami.com/script.js', 'data-website-id': 'YOUR_WEBSITE_ID' }]
]
})SPA 路由追踪
ts
// .vitepress/theme/index.ts
import DefaultTheme from 'vitepress/theme'
import { onMounted } from 'vue'
import { useRouter } from 'vitepress'
export default {
extends: DefaultTheme,
setup() {
if (typeof window !== 'undefined') {
const router = useRouter()
onMounted(() => {
router.onAfterRouteChanged = (to) => {
// @ts-expect-error umami 全局变量
if (typeof umami !== 'undefined') {
// @ts-expect-error umami 全局变量
umami.track()
}
}
})
}
}
}自定义事件追踪
ts
// 追踪代码复制
function trackCopy(language: string) {
// @ts-expect-error umami 全局变量
if (typeof umami !== 'undefined') {
// @ts-expect-error umami 全局变量
umami.track('code_copy', { language })
}
}
// 追踪搜索
function trackSearch(query: string) {
// @ts-expect-error umami 全局变量
if (typeof umami !== 'undefined') {
// @ts-expect-error umami 全局变量
umami.track('search', { query })
}
}Plausible
简单、隐私友好的分析工具,无 Cookie,完全符合 GDPR。
云服务配置
ts
// .vitepress/config.mts
export default defineConfig({
head: [
['script', { defer: '', 'data-domain': 'your-site.com', src: 'https://plausible.io/js/script.js' }]
]
})自托管配置
bash
# Docker Compose 部署
docker compose up -dyaml
# docker-compose.yml
version: '3'
services:
plausible:
image: plausible/analytics:latest
ports:
- "8000:8000"
environment:
- BASE_URL=https://analytics.your-site.com
- SECRET_KEY_BASE=your-secret-key
- DATABASE_URL=postgresql://plausible:plausible@db:5432/plausibleSPA 页面追踪
Plausible 的 script.hash.js 版本自动支持 SPA 路由追踪:
ts
// .vitepress/config.mts
export default defineConfig({
head: [
['script', { defer: '', 'data-domain': 'your-site.com', src: 'https://plausible.io/js/script.hash.js' }]
]
})Microsoft Clarity
微软提供的免费行为分析工具,支持热力图和会话录制。
基本配置
ts
// .vitepress/config.mts
export default defineConfig({
head: [
['script', { type: 'text/javascript' }, `(function(c,l,a,r,i,t,y){c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i;y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);})(window,document,"clarity","script","YOUR_PROJECT_ID");`]
]
})核心功能
| 功能 | 说明 |
|---|---|
| 热力图 | 可视化用户点击、滚动行为 |
| 会话录制 | 回放用户操作过程 |
| 死区分析 | 发现用户忽略的区域 |
| 互动分析 | 统计点击最多的元素 |
| 实时仪表板 | 查看当前在线用户行为 |
自定义标签
ts
// 标记用户类型
// @ts-expect-error clarity 全局变量
if (typeof clarity !== 'undefined') {
// @ts-expect-error clarity 全局变量
clarity('set', 'user_type', 'developer')
}多方案组合
有时一个方案无法满足所有需求,可以组合使用:
| 组合 | 优势 |
|---|---|
| Google Analytics + Clarity | GA 看数据指标,Clarity 看用户行为 |
| Umami + Clarity | 隐私友好的数据 + 免费热力图 |
| 百度统计 + GA | 国内 SEO 加分 + 国际分析 |
组合配置示例
ts
// .vitepress/config.mts
export default defineConfig({
head: [
// Google Analytics
['script', { async: '', src: 'https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX' }],
['script', {}, `window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}gtag('js',new Date());gtag('config','G-XXXXXXXXXX',{anonymize_ip:true});`],
// Microsoft Clarity
['script', { type: 'text/javascript' }, `(function(c,l,a,r,i,t,y){c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i;y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);})(window,document,"clarity","script","YOUR_PROJECT_ID");`]
]
})统计组件开发
访问计数徽章
vue
<!-- .vitepress/theme/components/PageViewBadge.vue -->
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useData } from 'vitepress'
const { page } = useData()
const pageViews = ref('---')
onMounted(async () => {
try {
// 使用 Umami API 获取页面浏览量
const res = await fetch(
`https://your-umami.com/api/websites/YOUR_WEBSITE_ID/stats?url=${page.value.relativePath}`,
{ headers: { Authorization: 'Bearer YOUR_API_KEY' } }
)
const data = await res.json()
pageViews.value = data.pageviews.value.toLocaleString()
} catch {
pageViews.value = '---'
}
})
</script>
<template>
<span class="page-view-badge">
👁 {{ pageViews }} 次浏览
</span>
</template>
<style scoped>
.page-view-badge {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 2px 8px;
font-size: 12px;
color: var(--vp-c-text-2);
background: var(--vp-c-bg-soft);
border-radius: 12px;
}
</style>阅读时间统计
vue
<!-- .vitepress/theme/components/ReadingTime.vue -->
<script setup lang="ts">
import { computed } from 'vue'
import { useData } from 'vitepress'
const { page } = useData()
const readingTime = computed(() => {
const content = page.value.content || ''
// 移除 frontmatter 和代码块
const text = content
.replace(/^---[\s\S]*?---/, '')
.replace(/```[\s\S]*?```/g, '')
.trim()
// 中文按字符计算,英文按单词计算
const chineseChars = (text.match(/[\u4e00-\u9fff]/g) || []).length
const englishWords = (text.match(/[a-zA-Z]+/g) || []).length
const totalWords = chineseChars + englishWords
// 中文阅读速度约 300 字/分钟,英文约 200 词/分钟
const minutes = chineseChars / 300 + englishWords / 200
if (minutes < 1) return '1 分钟'
if (minutes < 60) return `${Math.ceil(minutes)} 分钟`
return `${Math.floor(minutes / 60)} 小时 ${Math.ceil(minutes % 60)} 分钟`
})
</script>
<template>
<span class="reading-time">📖 {{ readingTime }}</span>
</template>进阶配置
自定义统计组件、Cookie 同意弹窗、隐私合规等进阶内容:
- 站点统计集成指南 — 自定义组件开发、阅读统计、访问计数 API
常见问题
统计数据不准确?
检查以下可能原因:
- SPA 路由追踪:确保配置了路由变化监听
- 广告拦截器:很多用户使用广告拦截器屏蔽了统计脚本
- Cookie 限制:隐私设置可能阻止统计脚本运行
- 机器人流量:搜索引擎爬虫会被统计为访问
国内用户无法加载 Google Analytics?
Google Analytics 在国内加载较慢甚至被屏蔽。解决方案:
- 切换到百度统计或 Umami
- 使用 Cloudflare Analytics 作为替代
- 通过国内 CDN 代理 GA 脚本(需自建)
如何满足 GDPR 要求?
- 获取用户同意后再加载统计脚本
- 使用
anonymize_ip隐藏 IP 地址 - 提供隐私政策页面说明数据收集情况
- 优先选择隐私友好的方案(Umami / Plausible)