Web Pixel 擴充功能
Web Pixel 擴充功能是 Shopify 以隱私為優先的顧客事件追蹤和分析方法。不同於將任意 JavaScript 注入店面(這會有效能下降和隱私侵犯的風險),Web Pixels 在沙箱化環境中運行,對顧客資料有受控的存取權限。它們接收標準化的電子商務事件,並可以在遵守顧客同意偏好的同時將資料轉發到分析平台。
傳統的追蹤腳本(Google Tag Manager、Facebook Pixel 等)擁有完整的頁面存取權限,可能會拖慢店面速度,且經常在未經適當同意的情況下收集資料。Shopify 的 Web Pixel 框架解決了這三個問題:沙箱化執行保障效能、標準化事件確保一致性,以及內建的同意管理實現隱私合規。
Web Pixels 的運作方式
Web pixels 在沙箱化的 Web Worker 中運行。它們無法存取 DOM、讀取 cookie 或直接與頁面互動。相反,它們訂閱 Shopify 在顧客瀏覽、新增商品到購物車和完成購買時發出的標準化顧客事件串流。
顧客事件
Shopify 發出一套全面的標準化電子商務事件。您的像素訂閱它所需的事件:
| 事件 | 觸發條件 | 關鍵資料 |
|---|---|---|
page_viewed | 顧客檢視任何頁面 | URL、頁面標題、參照來源 |
product_viewed | 顧客檢視產品頁面 | 產品 ID、標題、價格、變體 |
collection_viewed | 顧客檢視商品系列 | 商品系列 ID、標題 |
search_submitted | 顧客執行搜尋 | 搜尋查詢、結果數量 |
product_added_to_cart | 商品加入購物車 | 產品、變體、數量、購物車總計 |
product_removed_from_cart | 商品從購物車移除 | 產品、變體、數量 |
cart_viewed | 顧客檢視購物車頁面 | 購物車內容、總計 |
checkout_started | 顧客開始結帳 | 購物車內容、總計、折扣碼 |
checkout_address_info_submitted | 地址表單完成 | 國家、省份(無個人識別資訊) |
checkout_shipping_info_submitted | 運送方式已選擇 | 運送方式、費用 |
payment_info_submitted | 付款資訊已輸入 | 付款方式類型(無信用卡詳情) |
checkout_completed | 購買完成 | 訂單 ID、總計、明細項目、貨幣 |
顧客事件預設包含匿名化的資料。個人識別資訊(PII),如電子郵件地址和全名,僅在顧客已授予明確同意時才會包含。您的像素必須在存取 PII 欄位之前檢查 event.data.customer.consent。
建立 Web Pixel 擴充功能
# Generate a web pixel extension
shopify app generate extension --template web_pixel --name custom-analytics
extensions/custom-analytics/
├── src/
│ └── index.ts
├── shopify.extension.toml
└── package.json
擴充功能設定
# extensions/custom-analytics/shopify.extension.toml
api_version = "2025-01"
[[extensions]]
type = "web_pixel_extension"
name = "Custom Analytics Pixel"
handle = "custom-analytics"
[extensions.settings]
[[extensions.settings.fields]]
key = "analytics_endpoint"
type = "single_line_text_field"
name = "Analytics Endpoint URL"
description = "The URL where event data will be sent"
[[extensions.settings.fields]]
key = "api_key"
type = "single_line_text_field"
name = "API Key"
description = "Your analytics platform API key"
[[extensions.settings.fields]]
key = "track_search"
type = "boolean"
name = "Track Search Events"
description = "Enable tracking of search queries"
自訂像素實作
像素進入點訂閱事件並處理它們。以下是追蹤關鍵電子商務事件的完整實作:
// extensions/custom-analytics/src/index.ts
import { register } from '@shopify/web-pixels-extension';
register(({ analytics, browser, settings, init }) => {
const endpoint = settings.analytics_endpoint;
const apiKey = settings.api_key;
const trackSearch = settings.track_search === 'true';
// Helper to send events to your analytics backend
async function sendEvent(eventName: string, payload: Record<string, any>) {
try {
await fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': apiKey,
},
body: JSON.stringify({
event: eventName,
timestamp: new Date().toISOString(),
shopId: init.data.shop.id,
payload,
}),
// Use keepalive to ensure the request completes
// even if the customer navigates away
keepalive: true,
});
} catch (err) {
// Silently fail -- do not disrupt the customer experience
console.error(`Failed to send ${eventName} event:`, err);
}
}
// Track page views
analytics.subscribe('page_viewed', (event) => {
sendEvent('page_view', {
url: event.data.url,
title: event.data.pageTitle,
referrer: event.data.referrer,
});
});
// Track product views
analytics.subscribe('product_viewed', (event) => {
const product = event.data.productVariant;
sendEvent('product_view', {
productId: product?.product?.id,
productTitle: product?.product?.title,
variantId: product?.id,
variantTitle: product?.title,
price: product?.price?.amount,
currency: product?.price?.currencyCode,
});
});
// Track add to cart
analytics.subscribe('product_added_to_cart', (event) => {
const cartLine = event.data.cartLine;
sendEvent('add_to_cart', {
productId: cartLine?.merchandise?.product?.id,
productTitle: cartLine?.merchandise?.product?.title,
variantId: cartLine?.merchandise?.id,
quantity: cartLine?.quantity,
price: cartLine?.merchandise?.price?.amount,
currency: cartLine?.merchandise?.price?.currencyCode,
});
});
// Track cart removals
analytics.subscribe('product_removed_from_cart', (event) => {
const cartLine = event.data.cartLine;
sendEvent('remove_from_cart', {
productId: cartLine?.merchandise?.product?.id,
variantId: cartLine?.merchandise?.id,
quantity: cartLine?.quantity,
});
});
// Track checkout started
analytics.subscribe('checkout_started', (event) => {
const checkout = event.data.checkout;
sendEvent('begin_checkout', {
totalPrice: checkout?.totalPrice?.amount,
currency: checkout?.totalPrice?.currencyCode,
lineItemCount: checkout?.lineItems?.length,
discountCodes: checkout?.discountApplications?.map(
(d: any) => d.title
),
});
});
// Track purchase completed
analytics.subscribe('checkout_completed', (event) => {
const checkout = event.data.checkout;
sendEvent('purchase', {
orderId: checkout?.order?.id,
totalPrice: checkout?.totalPrice?.amount,
subtotalPrice: checkout?.subtotalPrice?.amount,
totalTax: checkout?.totalTax?.amount,
currency: checkout?.totalPrice?.currencyCode,
lineItems: checkout?.lineItems?.map((item: any) => ({
productId: item.variant?.product?.id,
productTitle: item.variant?.product?.title,
variantId: item.variant?.id,
quantity: item.quantity,
price: item.variant?.price?.amount,
})),
shippingRate: {
price: checkout?.shippingLine?.price?.amount,
},
});
});
// Conditionally track search
if (trackSearch) {
analytics.subscribe('search_submitted', (event) => {
sendEvent('search', {
query: event.data.searchResult?.query,
resultCount: event.data.searchResult?.productVariants?.length ?? 0,
});
});
}
// Track collection views
analytics.subscribe('collection_viewed', (event) => {
sendEvent('collection_view', {
collectionId: event.data.collection?.id,
collectionTitle: event.data.collection?.title,
});
});
});
符合隱私規範的分析
Shopify 的 Web Pixel 框架與顧客隱私同意系統整合。您的像素必須遵守同意狀態。
同意感知的事件處理
// Enhanced pixel with consent checking
register(({ analytics, browser, settings, init }) => {
const endpoint = settings.analytics_endpoint;
// Check initial consent status
let analyticsConsent = init.customerPrivacy?.analyticsProcessingAllowed ?? false;
let marketingConsent = init.customerPrivacy?.marketingAllowed ?? false;
// Listen for consent changes (customer updates preferences)
analytics.subscribe('visitor_consent_collected', (event) => {
analyticsConsent = event.data.analyticsAllowed;
marketingConsent = event.data.marketingAllowed;
});
async function sendEvent(
eventName: string,
payload: Record<string, any>,
requiresMarketingConsent: boolean = false
) {
// Always check consent before sending data
if (!analyticsConsent) return;
if (requiresMarketingConsent && !marketingConsent) return;
await fetch(endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
event: eventName,
timestamp: new Date().toISOString(),
consent: {
analytics: analyticsConsent,
marketing: marketingConsent,
},
payload,
}),
keepalive: true,
});
}
// Basic analytics events (require analytics consent)
analytics.subscribe('page_viewed', (event) => {
sendEvent('page_view', {
url: event.data.url,
title: event.data.pageTitle,
});
});
// Marketing-related events (require marketing consent)
analytics.subscribe('checkout_completed', (event) => {
// Send conversion data to ad platforms only with marketing consent
sendEvent(
'conversion',
{
orderId: event.data.checkout?.order?.id,
value: event.data.checkout?.totalPrice?.amount,
currency: event.data.checkout?.totalPrice?.currencyCode,
},
true // requires marketing consent
);
});
});
您的 Web Pixel 必須遵守 GDPR、CCPA 和其他隱私法規。關鍵要求:
- 未經同意不得收集 PII —— 即使事件資料中包含了 PII
- 遵守退出請求 —— 當顧客撤回同意時,立即停止傳送資料
- 資料最小化 —— 只收集您需要的資料。不要將原始事件有效負載轉發給第三方
- 保留期限 —— 確保您的分析後端按照您的隱私政策時間表刪除資料
違規可能導致您的應用程式從 Shopify App Store 中移除以及潛在的法律責任。
與第三方分析整合
Google Analytics 4
// GA4 integration via Measurement Protocol
register(({ analytics, settings }) => {
const measurementId = settings.ga4_measurement_id;
const apiSecret = settings.ga4_api_secret;
const GA4_ENDPOINT = `https://www.google-analytics.com/mp/collect?measurement_id=${measurementId}&api_secret=${apiSecret}`;
function sendToGA4(eventName: string, params: Record<string, any>) {
fetch(GA4_ENDPOINT, {
method: 'POST',
body: JSON.stringify({
client_id: generateClientId(), // Generate a privacy-safe client ID
events: [{ name: eventName, params }],
}),
keepalive: true,
});
}
analytics.subscribe('product_viewed', (event) => {
const product = event.data.productVariant;
sendToGA4('view_item', {
currency: product?.price?.currencyCode,
value: parseFloat(product?.price?.amount || '0'),
items: [{
item_id: product?.product?.id,
item_name: product?.product?.title,
item_variant: product?.title,
price: parseFloat(product?.price?.amount || '0'),
}],
});
});
analytics.subscribe('product_added_to_cart', (event) => {
const cartLine = event.data.cartLine;
sendToGA4('add_to_cart', {
currency: cartLine?.merchandise?.price?.currencyCode,
value: parseFloat(cartLine?.merchandise?.price?.amount || '0') * (cartLine?.quantity || 1),
items: [{
item_id: cartLine?.merchandise?.product?.id,
item_name: cartLine?.merchandise?.product?.title,
quantity: cartLine?.quantity,
price: parseFloat(cartLine?.merchandise?.price?.amount || '0'),
}],
});
});
analytics.subscribe('checkout_completed', (event) => {
const checkout = event.data.checkout;
sendToGA4('purchase', {
transaction_id: checkout?.order?.id,
currency: checkout?.totalPrice?.currencyCode,
value: parseFloat(checkout?.totalPrice?.amount || '0'),
tax: parseFloat(checkout?.totalTax?.amount || '0'),
shipping: parseFloat(checkout?.shippingLine?.price?.amount || '0'),
items: checkout?.lineItems?.map((item: any) => ({
item_id: item.variant?.product?.id,
item_name: item.variant?.product?.title,
quantity: item.quantity,
price: parseFloat(item.variant?.price?.amount || '0'),
})),
});
});
});
function generateClientId(): string {
// Generate a random client ID that does not contain PII
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(
/[xy]/g,
(c) => {
const r = (Math.random() * 16) | 0;
const v = c === 'x' ? r : (r & 0x3) | 0x8;
return v.toString(16);
}
);
}
Meta Conversions API
// Meta (Facebook) Conversions API integration
register(({ analytics, settings }) => {
const pixelId = settings.meta_pixel_id;
const accessToken = settings.meta_access_token;
const META_ENDPOINT = `https://graph.facebook.com/v18.0/${pixelId}/events`;
function sendToMeta(eventName: string, customData: Record<string, any>) {
fetch(META_ENDPOINT, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
data: [{
event_name: eventName,
event_time: Math.floor(Date.now() / 1000),
action_source: 'website',
custom_data: customData,
}],
access_token: accessToken,
}),
keepalive: true,
});
}
analytics.subscribe('product_viewed', (event) => {
const product = event.data.productVariant;
sendToMeta('ViewContent', {
content_ids: [product?.product?.id],
content_type: 'product',
content_name: product?.product?.title,
value: parseFloat(product?.price?.amount || '0'),
currency: product?.price?.currencyCode,
});
});
analytics.subscribe('product_added_to_cart', (event) => {
const cartLine = event.data.cartLine;
sendToMeta('AddToCart', {
content_ids: [cartLine?.merchandise?.product?.id],
content_type: 'product',
value: parseFloat(cartLine?.merchandise?.price?.amount || '0'),
currency: cartLine?.merchandise?.price?.currencyCode,
});
});
analytics.subscribe('checkout_completed', (event) => {
const checkout = event.data.checkout;
sendToMeta('Purchase', {
content_ids: checkout?.lineItems?.map(
(item: any) => item.variant?.product?.id
),
content_type: 'product',
value: parseFloat(checkout?.totalPrice?.amount || '0'),
currency: checkout?.totalPrice?.currencyCode,
num_items: checkout?.lineItems?.length,
});
});
});
測試和除錯
# Start development server with pixel debugging
shopify app dev
# The CLI provides a preview URL with pixel debugging enabled
# Open the browser console to see pixel event logs
除錯模式
register(({ analytics, settings, init }) => {
const debug = init.data.shop.myshopifyDomain.includes('dev');
function log(eventName: string, data: any) {
if (debug) {
console.log(`[Pixel Debug] ${eventName}:`, JSON.stringify(data, null, 2));
}
}
analytics.subscribe('page_viewed', (event) => {
log('page_viewed', event.data);
// ... normal processing
});
});
使用瀏覽器的 Network 分頁驗證您的像素是否正在向正確的端點傳送請求。Shopify 的開發工具還提供「Customer Events」除錯器,顯示頁面上發出的每個事件,以及哪些像素接收了它。透過開發商店上的 ?debug_pixels=true 查詢參數存取它。
效能考量
- 在所有 fetch 請求上使用
keepalive: true——這確保即使顧客在請求進行中離開,事件也會被傳送 - 盡可能批次處理事件——不要每個事件傳送一個請求,而是緩衝事件並每隔幾秒批次傳送
- 最小化有效負載大小——只包含您的分析平台需要的資料
- 避免同步模式——像素中的所有內容都應該是非阻塞的
- 靜默處理網路失敗——永遠不要讓追蹤失敗影響顧客體驗
// Event batching implementation
class EventBatcher {
private queue: any[] = [];
private timer: ReturnType<typeof setTimeout> | null = null;
private readonly endpoint: string;
private readonly batchSize: number;
private readonly flushInterval: number;
constructor(endpoint: string, batchSize = 10, flushInterval = 3000) {
this.endpoint = endpoint;
this.batchSize = batchSize;
this.flushInterval = flushInterval;
}
add(event: any) {
this.queue.push(event);
if (this.queue.length >= this.batchSize) {
this.flush();
} else if (!this.timer) {
this.timer = setTimeout(() => this.flush(), this.flushInterval);
}
}
private flush() {
if (this.timer) {
clearTimeout(this.timer);
this.timer = null;
}
if (this.queue.length === 0) return;
const batch = this.queue.splice(0, this.batchSize);
fetch(this.endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ events: batch }),
keepalive: true,
}).catch(() => {
// Re-queue failed events (with a retry limit)
// In production, add exponential backoff
});
}
}
Web Pixel 擴充功能提供了一種安全、高效能且符合隱私規範的方式來追蹤 Shopify 店面上的顧客行為。透過使用 Shopify 的標準化事件系統,您可以在所有商家之間獲得一致的資料,而不需要依賴脆弱的 DOM 抓取或 Cookie 追蹤。結合同意管理,您的分析整合已為隱私優先的網路做好準備。