Loop Polish
自动执行代码迭代优化,包含验证、评分、修复与报告生成。
自动为项目添加 Google Analytics 4 跟踪代码,支持多种框架。
openclaw skills install @jeftekhari/add-analytics命令、参数、文件名以原文为准
您正在为项目设置 Google Analytics 4 (GA4)。请遵循此完整指南,正确添加分析功能。
从 $ARGUMENTS 中解析以下内容:
G-XXXXXXXXXX(必填,若未提供则询问)扫描项目以确定框架/配置类型:
优先检测顺序:
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/ 目录)同时检查:
测量 ID 必须满足以下条件:
G- 开头(GA4 格式)G-ABC1234567如果用户提供了 UA- 格式的 ID,请告知:
“您提供的为 Universal Analytics ID(UA-)。GA4 使用以 'G-' 开头的测量 ID。
Universal Analytics 已于 2024 年 7 月停止服务。您需要在 analytics.google.com 创建一个新的 GA4 属性”
创建 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" />修改 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} />
</>
)
}创建 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()
}创建 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
}
}创建 plugins/analytics.client.ts
markdown
创建 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,
},
},
})创建 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>创建 src/lib/analytics.ts 和 src/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 />将以下代码添加到 <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>创建或更新 .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 测量 ID
NEXT_PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXXXXX重要提示:确保已将 .env.local 添加到 .gitignore 文件中。
创建一个完整的事件追踪工具集:
# Analytics 功能
## 事件追踪工具(GA4)
推荐事件参考:https://support.google.com/analytics/answer/9267735type 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,
})
}
}
## 第六步:Cookie 同意集成(若启用 `--consent` 标志)
创建具备同意状态感知的封装层: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<ConsentConfig>) => {
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_storage: 'denied',
ad_storage: 'denied',
ad_user_data: 'denied',
ad_personalization: 'denied',
})
}
// Cookie 工具函数
function setCookie(name: string, value: string, days: number) {
const date = new Date()
date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000)
document.cookie = ${name}=${value};expires=${date.toUTCString()};path=/;SameSite=Lax
}
function getCookie(name: string): string | null {
const match = document.cookie.match(new RegExp((^| )${name}=([^;]+)))
return match ? match[2] : null
}
## 第七步:调试模式(若启用 `--debug` 标志)
在开发环境中启用调试模式:// 开发环境启用调试模式
if (process.env.NODE_ENV === 'development') {
window.gtag('config', 'G-XXXXXXXXXX', {
debug_mode: true,
})
}
建议安装 Chrome 浏览器插件 [Google Analytics Debugger](https://chrome.google.com/webstore/detail/google-analytics-debugger/jnkmfdileelhofjcijamephohjechhna) 以辅助调试。
## 步骤 8:TypeScript 类型声明
如果使用 TypeScript,请创建 `types/gtag.d.ts` 文件:// types/gtag.d.ts
declare global {
interface Window {
gtag: Gtag.Gtag
dataLayer: object[]
}
}
declare namespace Gtag {
interface Gtag {
(command: 'config', targetId: string, config?: ConfigParams): void
(command: 'set', targetId: string, config: ConfigParams): void
(command: 'set', config: ConfigParams): void
(command: 'js', date: Date): void
(command: 'event', eventName: string, eventParams?: EventParams): void
(command: 'consent', consentArg: 'default' | 'update', consentParams: ConsentParams): void
(...args: unknown[]): void
}
interface ConfigParams {
page_title?: string
page_location?: string
page_path?: string
send_page_view?: boolean
debug_mode?: boolean
[key: string]: unknown
}
interface EventParams {
event_category?: string
event_label?: string
value?: number
[key: string]: unknown
}
interface ConsentParams {
analytics_storage?: 'granted' | 'denied'
ad_storage?: 'granted' | 'denied'
ad_user_data?: 'granted' | 'denied'
ad_personalization?: 'granted' | 'denied'
wait_for_update?: number
}
}
export {}
## 步骤 9:验证检查清单
完成实现后,请进行以下验证:
1. [ ] 测量 ID 格式正确(G-XXXXXXXXXX)
2. [ ] 生产环境脚本已加载(检查网络标签页)
3. [ ] GA4 仪表板中的实时报告显示活动数据
4. [ ] 页面浏览在导航时被正常追踪
5. [ ] 控制台无与 gtag 相关的错误
6. [ ] 环境变量未提交到 Git
7. [ ] TypeScript 无类型错误(如适用)
## 步骤 10:总结输出
完成设置后,请向用户提供以下信息:
1. **创建或修改的文件**(列出文件名)
2. **所需的环境变量**(附示例值)
3. **下一步操作**:
- 将测量 ID 添加至环境变量
- 部署并验证 GA4 实时报告中的数据
- 在 GA4 仪表板中设置转化事件
- 考虑为关键用户行为添加自定义事件
## 常见问题与解决方案
**“gtag 未定义”**
- 脚本尚未加载;请确保异步加载逻辑正确处理
**GA4 中无数据**
- 检查广告拦截器是否阻止了跟踪
- 确认测量 ID 是否正确
- 查看浏览器控制台是否存在错误
**页面浏览重复计数**
- 单页应用路由触发重复事件;需实现去重机制
**GDPR 合规性**
- 对欧盟用户必须启用同意模式
- 使用 --consent 标志添加同意管理功能已收录 1 个 Skill