跳至主要內容
Skip to content

GA4 完整整合指南:從基礎設定到 Vue Composable 封裝

Google Analytics 4(GA4)是 Google 推出的新一代網站分析工具,採用以「事件」為核心的資料模型,取代了傳統 Universal Analytics 的「工作階段」概念。本篇將完整介紹如何在前端專案中整合 GA4,從基礎設定到進階的 Vue Composable 封裝。


一、 GA4 核心概念

1.1 事件驅動架構

GA4 的資料模型以「事件(Event)」為核心:

  • 自動收集事件:頁面瀏覽、首次造訪、工作階段開始等
  • 加強型評估事件:捲動、外連點擊、網站搜尋、影片互動等
  • 建議事件:Google 定義的標準事件,如 loginpurchase
  • 自訂事件:開發者自行定義的事件

1.2 gtag.js API 架構

gtag() 是 Google Tag 的核心函式,支援以下指令:

指令用途範例
config配置目標(GA4 資源)gtag('config', 'G-XXXXXXXXXX')
set設定全域參數gtag('set', { currency: 'TWD' })
event傳送事件gtag('event', 'login', { method: 'Google' })
consent管理使用者同意權gtag('consent', 'update', {...})
get取得參數值gtag('get', 'G-XXXXXXXXXX', 'client_id', callback)

二、 基礎設定

2.1 安裝 Google Tag

在 HTML <head> 中加入以下程式碼:

html
<!-- Google tag (gtag.js) -->
<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>

IMPORTANT

G-XXXXXXXXXX 替換為你的 GA4 測量 ID(Measurement ID),可在 GA4 管理介面的「資料串流」中找到。

2.2 在 VitePress 中整合

VitePress 透過 config.tshead 設定注入標籤:

typescript
// .vitepress/config.ts
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');`,
    ],
  ],
});

2.3 在 Vue / Nuxt 中整合

對於 SPA(Single Page Application,單頁應用程式)框架,需要處理路由變化時的頁面瀏覽追蹤:

typescript
// plugins/ga4.ts (Nuxt 3 範例)
export default defineNuxtPlugin((nuxtApp) => {
  const config = useRuntimeConfig();
  const measurementId = config.public.ga4MeasurementId;

  if (!measurementId) return;

  // 注入 gtag.js
  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}', { send_page_view: false });
        `,
      },
    ],
  });

  // 監聽路由變化
  const router = useRouter();
  router.afterEach((to) => {
    window.gtag?.("event", "page_view", {
      page_path: to.fullPath,
      page_title: document.title,
    });
  });
});

TIP

設定 send_page_view: false 可避免初始頁面重複計算,改由路由監聽統一處理。


三、 事件追蹤

3.1 基本語法

javascript
gtag('event', '<事件名稱>', {
  // 事件參數
  <參數名稱>: <參數值>,
})

3.2 建議事件範例

Google 定義了一系列建議事件,使用這些事件可獲得更好的報表支援:

javascript
// 登入事件
gtag("event", "login", {
  method: "Google",
});

// 註冊事件
gtag("event", "sign_up", {
  method: "Email",
});

// 分享事件
gtag("event", "share", {
  method: "Twitter",
  content_type: "article",
  item_id: "article_123",
});

// 搜尋事件
gtag("event", "search", {
  search_term: "Vue composable",
});

3.3 自訂事件

javascript
// 自訂事件:影片播放
gtag("event", "video_play", {
  video_title: "教學影片第一集",
  video_duration: 300,
  video_provider: "YouTube",
});

// 自訂事件:表單提交
gtag("event", "form_submit", {
  form_name: "contact_form",
  form_destination: "/thank-you",
});

3.4 參數範圍與優先序

gtag 的參數可以在三個層級設定:

優先序event > config > set

javascript
// 全域設定(最低優先)
gtag("set", { currency: "USD" });

// 目標設定
gtag("config", "G-XXXXXXXXXX", { currency: "EUR" });

// 事件設定(最高優先)
gtag("event", "purchase", {
  currency: "TWD", // 此事件會使用 TWD
  value: 1000,
});

四、 使用者屬性

使用者屬性(User Properties)可用於區隔使用者群組,每個專案最多可設定 25 個自訂屬性。

4.1 設定使用者屬性

javascript
gtag("set", "user_properties", {
  membership_level: "gold",
  favorite_category: "electronics",
  account_type: "premium",
});

4.2 保留名稱

以下名稱為系統保留,不可使用:

  • first_open_time
  • first_visit_time
  • last_deep_link_referrer
  • user_id
  • first_open_after_install

此外,屬性名稱不可以以下前綴開頭:

  • google_
  • ga_
  • firebase_

4.3 設定 User ID

若有會員系統,可設定 User ID 進行跨裝置追蹤:

javascript
gtag("config", "G-XXXXXXXXXX", {
  user_id: "USER_12345",
});

WARNING

User ID 必須是不可識別個人身份的 ID(如會員編號),絕對不可使用 Email 或電話號碼。


五、 電商追蹤

GA4 提供完整的電商追蹤事件,涵蓋使用者的整個購物流程。

5.1 電商事件流程

5.2 商品資料結構

javascript
const items = [
  {
    item_id: "SKU_12345",
    item_name: "Vue 3 實戰指南",
    affiliation: "Hirimu 書店",
    coupon: "SUMMER_SALE",
    discount: 50,
    index: 0,
    item_brand: "Hirimu",
    item_category: "程式設計",
    item_category2: "前端開發",
    item_variant: "電子書",
    price: 450,
    quantity: 1,
  },
];

5.3 加入購物車事件

javascript
gtag("event", "add_to_cart", {
  currency: "TWD",
  value: 450,
  items: [
    {
      item_id: "SKU_12345",
      item_name: "Vue 3 實戰指南",
      price: 450,
      quantity: 1,
    },
  ],
});

5.4 購買完成事件

javascript
gtag("event", "purchase", {
  transaction_id: "T_12345",
  value: 850,
  tax: 42.5,
  shipping: 60,
  currency: "TWD",
  coupon: "SUMMER_SALE",
  items: [
    {
      item_id: "SKU_12345",
      item_name: "Vue 3 實戰指南",
      price: 450,
      quantity: 1,
    },
    {
      item_id: "SKU_12346",
      item_name: "TypeScript 入門",
      price: 400,
      quantity: 1,
    },
  ],
});

六、 Debug 模式

6.1 啟用 Debug 模式

在 config 中加入 debug_mode 參數:

javascript
gtag("config", "G-XXXXXXXXXX", {
  debug_mode: true,
});

或針對單一事件啟用:

javascript
gtag("event", "test_event", {
  debug_mode: true,
  // 其他參數...
});

6.2 使用 DebugView

  1. 開啟 GA4 管理介面
  2. 前往「設定」→「DebugView」
  3. 可即時查看來自啟用 debug_mode 裝置的事件

6.3 Chrome 擴充功能

安裝 Google Analytics Debugger 擴充功能,可在 Console 中查看詳細的追蹤資訊。


七、 Vue Composable 封裝

將 GA4 功能封裝為 Vue Composable,提供類型安全與更好的開發體驗。

7.1 類型定義

typescript
// types/useGA4.ts
export interface GA4Config {
  measurementId: string;
  debug?: boolean;
}

export interface EventParams {
  [key: string]: string | number | boolean | undefined;
}

export interface EcommerceItem {
  item_id: string;
  item_name: string;
  price?: number;
  quantity?: number;
  item_brand?: string;
  item_category?: string;
  item_variant?: string;
  coupon?: string;
  discount?: number;
  index?: number;
}

export interface PurchaseParams {
  transaction_id: string;
  value: number;
  currency: string;
  tax?: number;
  shipping?: number;
  coupon?: string;
  items: EcommerceItem[];
}

export interface UseGA4Return {
  isReady: Ref<boolean>;
  trackEvent: (eventName: string, params?: EventParams) => void;
  trackPageView: (pagePath: string, pageTitle?: string) => void;
  setUserProperties: (properties: Record<string, string | number>) => void;
  setUserId: (userId: string | null) => void;
  trackPurchase: (params: PurchaseParams) => void;
  trackAddToCart: (item: EcommerceItem, currency?: string) => void;
}

7.2 完整實作

typescript
// composables/useGA4.ts
import { ref, onMounted } from "vue";
import type {
  GA4Config,
  EventParams,
  PurchaseParams,
  EcommerceItem,
  UseGA4Return,
} from "@/types/useGA4";

// 擴展 Window 類型
declare global {
  interface Window {
    gtag?: (...args: unknown[]) => void;
    dataLayer?: unknown[];
  }
}

export function useGA4(config: GA4Config): UseGA4Return {
  const isReady = ref(false);
  const { measurementId, debug = false } = config;

  // 初始化 gtag
  function initGtag() {
    if (typeof window === "undefined") return;

    // 建立 dataLayer
    window.dataLayer = window.dataLayer || [];
    window.gtag = function gtag() {
      window.dataLayer!.push(arguments);
    };

    // 載入 gtag.js
    const script = document.createElement("script");
    script.async = true;
    script.src = `https://www.googletagmanager.com/gtag/js?id=${measurementId}`;
    script.onload = () => {
      isReady.value = true;
    };
    document.head.appendChild(script);

    // 初始化配置
    window.gtag("js", new Date());
    window.gtag("config", measurementId, {
      debug_mode: debug,
      send_page_view: false, // 由 trackPageView 手動控制
    });
  }

  // 追蹤自訂事件
  function trackEvent(eventName: string, params: EventParams = {}) {
    if (!window.gtag) {
      console.warn("[GA4] gtag 尚未初始化");
      return;
    }

    const eventParams = debug ? { ...params, debug_mode: true } : params;
    window.gtag("event", eventName, eventParams);

    if (debug) {
      console.log("[GA4] Event:", eventName, eventParams);
    }
  }

  // 追蹤頁面瀏覽
  function trackPageView(pagePath: string, pageTitle?: string) {
    trackEvent("page_view", {
      page_path: pagePath,
      page_title: pageTitle || document.title,
    });
  }

  // 設定使用者屬性
  function setUserProperties(properties: Record<string, string | number>) {
    if (!window.gtag) return;

    window.gtag("set", "user_properties", properties);

    if (debug) {
      console.log("[GA4] User Properties:", properties);
    }
  }

  // 設定 User ID
  function setUserId(userId: string | null) {
    if (!window.gtag) return;

    window.gtag("config", measurementId, {
      user_id: userId,
    });

    if (debug) {
      console.log("[GA4] User ID:", userId);
    }
  }

  // 追蹤購買事件
  function trackPurchase(params: PurchaseParams) {
    trackEvent("purchase", params as unknown as EventParams);
  }

  // 追蹤加入購物車
  function trackAddToCart(item: EcommerceItem, currency = "TWD") {
    const value = (item.price || 0) * (item.quantity || 1);

    trackEvent("add_to_cart", {
      currency,
      value,
      items: [item],
    } as unknown as EventParams);
  }

  onMounted(() => {
    initGtag();
  });

  return {
    isReady,
    trackEvent,
    trackPageView,
    setUserProperties,
    setUserId,
    trackPurchase,
    trackAddToCart,
  };
}

7.3 元件中使用

vue
<script setup lang="ts">
import { useGA4 } from "@/composables/useGA4";
import { useRouter } from "vue-router";

const { trackEvent, trackPageView, setUserId } = useGA4({
  measurementId: "G-XXXXXXXXXX",
  debug: import.meta.env.DEV,
});

const router = useRouter();

// 路由變化時追蹤頁面
router.afterEach((to) => {
  trackPageView(to.fullPath);
});

// 使用者登入後設定 ID
function onLogin(userId: string) {
  setUserId(userId);
  trackEvent("login", { method: "email" });
}

// 按鈕點擊追蹤
function onCtaClick() {
  trackEvent("cta_click", {
    button_text: "立即購買",
    button_location: "hero_section",
  });
}
</script>

<template>
  <button @click="onCtaClick">立即購買</button>
</template>

7.4 全域提供

typescript
// plugins/ga4.ts (Vue 3)
import { useGA4 } from "@/composables/useGA4";
import type { App, InjectionKey } from "vue";
import type { UseGA4Return } from "@/types/useGA4";

export const GA4_KEY: InjectionKey<UseGA4Return> = Symbol("ga4");

export function setupGA4(app: App, measurementId: string) {
  const ga4 = useGA4({
    measurementId,
    debug: import.meta.env.DEV,
  });

  app.provide(GA4_KEY, ga4);
}

// 在子元件中使用
// const ga4 = inject(GA4_KEY)

八、 技術實務與相容性

8.1 瀏覽器支援

GA4(gtag.js)支援所有現代瀏覽器。對於不支援 JavaScript 的環境,可使用 Measurement Protocol 進行伺服器端追蹤。

javascript
// 預設拒絕追蹤(GDPR 合規)
gtag("consent", "default", {
  analytics_storage: "denied",
  ad_storage: "denied",
});

// 使用者同意後更新
gtag("consent", "update", {
  analytics_storage: "granted",
  ad_storage: "granted",
});

8.3 效能考量

javascript
// 避免阻塞載入
gtag("config", "G-XXXXXXXXXX", {
  transport_type: "beacon", // 使用 navigator.sendBeacon
});

8.4 錯誤處理

typescript
function safeTrackEvent(eventName: string, params?: EventParams) {
  try {
    if (typeof window !== "undefined" && window.gtag) {
      window.gtag("event", eventName, params);
    }
  } catch (error) {
    console.error("[GA4] 追蹤事件時發生錯誤:", error);
  }
}

總結

概念說明
gtag()Google Tag 核心 API 函式
config配置 GA4 測量 ID
event傳送事件資料
set設定全域參數
User Properties使用者屬性區隔
Ecommerce Events電商追蹤事件
Debug Mode即時偵錯模式

TIP

最佳實踐

  • 在開發環境中啟用 debug_mode 進行測試
  • 使用建議事件名稱以獲得更好的報表支援
  • 將 GA4 封裝為 Composable 提供類型安全
  • 實作 Consent Mode 以符合隱私法規

進階挑戰

  1. 實作一個自動追蹤所有連結點擊的 Vue 指令(v-track-click
  2. 結合 Measurement Protocol 實作伺服器端追蹤
  3. 建立一個 A/B 測試框架,將實驗資料傳送至 GA4

延伸閱讀與資源