OA0 = Omni AI 0
OA0 是一个探索 AI 的论坛
现在注册
已注册用户请  登录
OA0  ›  技能包  ›  add-analytics:为任何 Web 项目集成 Google Analytics 4 追踪

add-analytics:为任何 Web 项目集成 Google Analytics 4 追踪

 
  jwt ·  2026-02-05 19:25:32 · 3 次点击  · 0 条评论  

名称: add-analytics
描述: 为任意项目添加 Google Analytics 4 跟踪。自动检测框架、添加跟踪代码、设置事件并配置隐私设置。
argument-hint: " [--events] [--consent] [--debug]"


Google Analytics 4 设置指南

本指南将帮助您为项目正确配置 Google Analytics 4 (GA4)。

参数说明

$ARGUMENTS 中解析以下参数:
- Measurement ID:格式为 G-XXXXXXXXXX(必需,如未提供则询问)
- --events:包含自定义事件跟踪辅助工具
- --consent:包含 Cookie 同意集成
- --debug:启用开发调试模式

步骤 1:检测项目类型

扫描项目以确定框架/配置:

优先级检测顺序:
1. next.config.js/ts → Next.js
2. nuxt.config.js/ts → Nuxt.js
3. astro.config.mjs → Astro
4. svelte.config.js → SvelteKit
5. remix.config.js → Remix
6. gatsby-config.js → Gatsby
7. vite.config.js + src/App.vue → Vue + Vite
8. vite.config.js + src/App.tsx → React + Vite
9. angular.json → Angular
10. package.json 中包含 "react-scripts" → Create React App
11. 仅有 index.html → 纯 HTML
12. _app.tsx/jsx → Next.js(检查 App Router:app/ 目录)

同时检查:
- TypeScript 使用情况(tsconfig.json)
- 现有分析工具(搜索 gtag、GA、analytics)
- 包管理器(pnpm-lock.yaml、yarn.lock、package-lock.json)

步骤 2:验证 Measurement ID

Measurement ID 必须:
- 以 G- 开头(GA4 格式)
- 后跟恰好 10 个字母数字字符
- 示例:G-ABC1234567

如果用户提供 UA- 格式的 ID,请告知:

“您提供的是 Universal Analytics ID (UA-)。GA4 使用以 'G-' 开头的 Measurement ID。
Universal Analytics 已于 2024 年 7 月停止服务。您需要在 analytics.google.com 上创建一个 GA4 属性。”

步骤 3:按框架实施

Next.js(App Router - app/ 目录)

创建 app/layout.tsx 修改或创建 components/GoogleAnalytics.tsx

// components/GoogleAnalytics.tsx
'use client'

import Script from 'next/script'

interface GoogleAnalyticsProps {
  measurementId: string
}

export function GoogleAnalytics({ measurementId }: GoogleAnalyticsProps) {
  return (
    <>
      <Script
        src={`https://www.googletagmanager.com/gtag/js?id=${measurementId}`}
        strategy="afterInteractive"
      />
      <Script id="google-analytics" strategy="afterInteractive">
        {`
          window.dataLayer = window.dataLayer || [];
          function gtag(){dataLayer.push(arguments);}
          gtag('js', new Date());
          gtag('config', '${measurementId}');
        `}
      </Script>
    </>
  )
}

添加到根布局:

// app/layout.tsx
import { GoogleAnalytics } from '@/components/GoogleAnalytics'

// 在 <body> 或 <html> 内添加:
<GoogleAnalytics measurementId="G-XXXXXXXXXX" />

Next.js(Pages Router - pages/ 目录)

修改 pages/_app.tsx

// pages/_app.tsx
import type { AppProps } from 'next/app'
import Script from 'next/script'

const GA_MEASUREMENT_ID = process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID

export default function App({ Component, pageProps }: AppProps) {
  return (
    <>
      <Script
        src={`https://www.googletagmanager.com/gtag/js?id=${GA_MEASUREMENT_ID}`}
        strategy="afterInteractive"
      />
      <Script id="google-analytics" strategy="afterInteractive">
        {`
          window.dataLayer = window.dataLayer || [];
          function gtag(){dataLayer.push(arguments);}
          gtag('js', new Date());
          gtag('config', '${GA_MEASUREMENT_ID}');
        `}
      </Script>
      <Component {...pageProps} />
    </>
  )
}

React(Vite/CRA)

创建 src/lib/analytics.ts

// src/lib/analytics.ts
export const GA_MEASUREMENT_ID = import.meta.env.VITE_GA_MEASUREMENT_ID

declare global {
  interface Window {
    gtag: (...args: unknown[]) => void
    dataLayer: unknown[]
  }
}

export const initGA = () => {
  if (typeof window === 'undefined') return

  const script = document.createElement('script')
  script.src = `https://www.googletagmanager.com/gtag/js?id=${GA_MEASUREMENT_ID}`
  script.async = true
  document.head.appendChild(script)

  window.dataLayer = window.dataLayer || []
  window.gtag = function gtag() {
    window.dataLayer.push(arguments)
  }
  window.gtag('js', new Date())
  window.gtag('config', GA_MEASUREMENT_ID)
}

export const pageview = (url: string) => {
  window.gtag('config', GA_MEASUREMENT_ID, {
    page_path: url,
  })
}

export const event = (action: string, params?: Record<string, unknown>) => {
  window.gtag('event', action, params)
}

src/main.tsx 中初始化:

import { initGA } from './lib/analytics'

// 在渲染前初始化
if (import.meta.env.PROD) {
  initGA()
}

Vue 3(Vite)

创建 src/plugins/analytics.ts

// src/plugins/analytics.ts
import type { App } from 'vue'
import type { Router } from 'vue-router'

const GA_MEASUREMENT_ID = import.meta.env.VITE_GA_MEASUREMENT_ID

declare global {
  interface Window {
    gtag: (...args: unknown[]) => void
    dataLayer: unknown[]
  }
}

export const analyticsPlugin = {
  install(app: App, { router }: { router: Router }) {
    // 加载 gtag 脚本
    const script = document.createElement('script')
    script.src = `https://www.googletagmanager.com/gtag/js?id=${GA_MEASUREMENT_ID}`
    script.async = true
    document.head.appendChild(script)

    window.dataLayer = window.dataLayer || []
    window.gtag = function gtag() {
      window.dataLayer.push(arguments)
    }
    window.gtag('js', new Date())
    window.gtag('config', GA_MEASUREMENT_ID)

    // 跟踪路由变化
    router.afterEach((to) => {
      window.gtag('config', GA_MEASUREMENT_ID, {
        page_path: to.fullPath,
      })
    })

    // 提供全局方法
    app.config.globalProperties.$gtag = window.gtag
  }
}

Nuxt 3

创建 plugins/analytics.client.ts

// plugins/analytics.client.ts
export default defineNuxtPlugin(() => {
  const config = useRuntimeConfig()
  const measurementId = config.public.gaMeasurementId

  if (!measurementId) return

  // 加载 gtag
  useHead({
    script: [
      {
        src: `https://www.googletagmanager.com/gtag/js?id=${measurementId}`,
        async: true,
      },
      {
        innerHTML: `
          window.dataLayer = window.dataLayer || [];
          function gtag(){dataLayer.push(arguments);}
          gtag('js', new Date());
          gtag('config', '${measurementId}');
        `,
      },
    ],
  })

  // 跟踪路由变化
  const router = useRouter()
  router.afterEach((to) => {
    window.gtag('config', measurementId, {
      page_path: to.fullPath,
    })
  })
})

添加到 nuxt.config.ts

export default defineNuxtConfig({
  runtimeConfig: {
    public: {
      gaMeasurementId: process.env.NUXT_PUBLIC_GA_MEASUREMENT_ID,
    },
  },
})

Astro

创建 src/components/Analytics.astro

---
// src/components/Analytics.astro
interface Props {
  measurementId: string
}

const { measurementId } = Astro.props
---

<script
  is:inline
  define:vars={{ measurementId }}
  src={`https://www.googletagmanager.com/gtag/js?id=${measurementId}`}
></script>

<script is:inline define:vars={{ measurementId }}>
  window.dataLayer = window.dataLayer || [];
  function gtag() {
    dataLayer.push(arguments);
  }
  gtag('js', new Date());
  gtag('config', measurementId);
</script>

添加到布局:

---
import Analytics from '../components/Analytics.astro'
---
<html>
  <head>
    <Analytics measurementId="G-XXXXXXXXXX" />
  </head>
</html>

SvelteKit

创建 src/lib/analytics.tssrc/routes/+layout.svelte

// src/lib/analytics.ts
import { browser } from '$app/environment'

export const GA_MEASUREMENT_ID = import.meta.env.VITE_GA_MEASUREMENT_ID

export function initGA() {
  if (!browser) return

  const script = document.createElement('script')
  script.src = `https://www.googletagmanager.com/gtag/js?id=${GA_MEASUREMENT_ID}`
  script.async = true
  document.head.appendChild(script)

  window.dataLayer = window.dataLayer || []
  window.gtag = function gtag() {
    window.dataLayer.push(arguments)
  }
  window.gtag('js', new Date())
  window.gtag('config', GA_MEASUREMENT_ID)
}

export function trackPageview(url: string) {
  if (!browser) return
  window.gtag('config', GA_MEASUREMENT_ID, { page_path: url })
}
<!-- src/routes/+layout.svelte -->
<script lang="ts">
  import { onMount } from 'svelte'
  import { page } from '$app/stores'
  import { initGA, trackPageview } from '$lib/analytics'

  onMount(() => {
    initGA()
  })

  $: if ($page.url.pathname) {
    trackPageview($page.url.pathname)
  }
</script>

<slot />

纯 HTML

添加到 <head>

<!-- Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());
  gtag('config', 'G-XXXXXXXXXX');
</script>

步骤 4:环境变量

创建或更新 .env / .env.local

# Next.js 使用
NEXT_PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXXXXX

# Vite (React/Vue/Svelte) 使用
VITE_GA_MEASUREMENT_ID=G-XXXXXXXXXX

# Nuxt 使用
NUXT_PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXXXXX

如果存在 .env.example,请添加(不包含实际 ID):

# Google Analytics 4 Measurement ID
NEXT_PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXXXXX

重要:如果尚未包含,请将 .env.local 添加到 .gitignore

步骤 5:事件跟踪辅助工具(如果使用 --events 标志)

创建全面的事件工具集:

// lib/analytics-events.ts

/**
 * GA4 事件跟踪工具
 *
 * 推荐事件:https://support.google.com/analytics/answer/9267735
 */

type GTagEvent = {
  action: string
  category?: string
  label?: string
  value?: number
  [key: string]: unknown
}

// 核心事件函数
export const trackEvent = ({ action, category, label, value, ...rest }: GTagEvent) => {
  if (typeof window === 'undefined' || !window.gtag) return

  window.gtag('event', action, {
    event_category: category,
    event_label: label,
    value,
    ...rest,
  })
}

// 互动事件
export const trackClick = (elementName: string, location?: string) => {
  trackEvent({
    action: 'click',
    category: 'engagement',
    label: elementName,
    click_location: location,
  })
}

export const trackScroll = (percentage: number) => {
  trackEvent({
    action: 'scroll',
    category: 'engagement',
    value: percentage,
  })
}

// 转化事件
export const trackSignUp = (method: string) => {
  trackEvent({
    action: 'sign_up',
    method,
  })
}

export const trackLogin = (method: string) => {
  trackEvent({
    action: 'login',
    method,
  })
}

export const trackPurchase = (params: {
  transactionId: string
  value: number
  currency: string
  items?: Array<{
    itemId: string
    itemName: string
    price: number
    quantity: number
  }>
}) => {
  trackEvent({
    action: 'purchase',
    transaction_id: params.transactionId,
    value: params.value,
    currency: params.currency,
    items: params.items,
  })
}

// 内容事件
export const trackSearch = (searchTerm: string) => {
  trackEvent({
    action: 'search',
    search_term: searchTerm,
  })
}

export const trackShare = (method: string, contentType: string, itemId: string) => {
  trackEvent({
    action: 'share',
    method,
    content_type: contentType,
    item_id: itemId,
  })
}

// 表单事件
export const trackFormStart = (formName: string) => {
  trackEvent({
    action: 'form_start',
    form_name: formName,
  })
}

export const trackFormSubmit = (formName: string, success: boolean) => {
  trackEvent({
    action: 'form_submit',
    form_name: formName,
    success,
  })
}

// 错误跟踪
export const trackError = (errorMessage: string, errorLocation?: string) => {
  trackEvent({
    action: 'exception',
    description: errorMessage,
    fatal: false,
    error_location: errorLocation,
  })
}

// 自定义事件构建器,提供灵活性
export const createCustomEvent = (eventName: string) => {
  return (params?: Record<string, unknown>) => {
    trackEvent({
      action: eventName,
      ...params,
    })
  }
}

创建支持同意的包装器:

```typescript
// lib/analytics-consent.ts

type ConsentState = 'granted' | 'denied'

interface ConsentConfig {
analytics_storage: ConsentState
ad_storage: ConsentState
ad_user_data: ConsentState
ad_personalization: ConsentState
}

const CONSENT_COOKIE = 'analytics_consent'

// 使用同意模式初始化
export const initWithConsent = (measurementId: string) => {
if (typeof window === 'undefined') return

// 设置默认同意状态(用户同意前为拒绝)
window.gtag('consent', 'default', {
analytics_storage: 'denied',
ad_storage: 'denied',
ad_user_data: 'denied',
ad_personalization: 'denied',
wait_for_update: 500, // 等待同意横幅
})

// 加载 gtag
const script = document.createElement('script')
script.src = https://www.googletagmanager.com/gtag/js?id=${measurementId}
script.async = true
document.head.appendChild(script)

window.dataLayer = window.dataLayer || []
window.gtag = function gtag() {
window.dataLayer.push(arguments)
}
window.gtag('js', new Date())
window.gtag('config', measurementId)

// 检查现有同意状态
const savedConsent = getCookie(CONSENT_COOKIE)
if (savedConsent) {
updateConsent(JSON.parse(savedConsent))
}
}

// 用户做出选择时更新同意状态
export const updateConsent = (consent: Partial) => {
if (typeof window === 'undefined' || !window.gtag) return

const consentState: ConsentConfig = {
analytics_storage: consent.analytics_storage || 'denied',
ad_storage: consent.ad_storage || 'denied',
ad_user_data: consent.ad_user_data || 'denied',
ad_personalization: consent.ad_personalization || 'denied',
}

window.gtag('consent', 'update', consentState)

// 保存到 Cookie
setCookie(CONSENT_COOKIE, JSON.stringify(consentState), 365)
}

// 便捷函数
export const acceptAll = () => {
updateConsent({
analytics_storage: 'granted',
ad_storage: 'granted',
ad_user_data: 'granted',
ad_personalization: 'granted',
})
}

export const acceptAnalyticsOnly = () => {
updateConsent({
analytics_storage: 'granted',
ad_storage: 'denied',
ad_user_data: 'denied',
ad_personalization: 'denied',
})
}

export const denyAll = () => {
updateConsent({
analytics_st

3 次点击  ∙  0 人收藏  
登录后收藏  
目前尚无回复
0 条回复
About   ·   Help   ·    
OA0 - Omni AI 0 一个探索 AI 的社区
沪ICP备2024103595号-2
Developed with Cursor