vue-analytics-kit
| Install | |
|---|---|
npm install vue-analytics-kit |
|
| Latest Version: | 0.1.0 |
| Vue: | 3 |
| License: | MIT |
| Last Updated: | Oct 5, 2026 |
| Links: | GitHub · npm |
vue-analytics-kit
Tek track() çağrısı, birçok hedef: GA4, Google Tag Manager, Google Ads, Meta Pixel, TikTok Pixel ve Firebase Analytics için onay (consent) bilinçli Vue 3 eklentisi.
analytics.track('purchase', {
transaction_id: 'ORD-1042',
value: 199.9,
currency: 'TRY',
items: [{ item_id: 'SKU-A', quantity: 2 }],
});
Bu tek çağrı GA4 için gtag('event', 'purchase', …), Google Ads için bir conversion, Meta için fbq('trackSingle', …, 'Purchase', …), TikTok için ttq.instance(…).track('Purchase', …) olur. Her biri sağlayıcının kendi biçimindedir, hepsi aynı olay kimliğini taşır ve yalnızca ziyaretçinin izin verdiği sağlayıcılara gider.
Durum: ön sürüm. Paket kendi test paketiyle korunuyor ve istek biçimleri her sağlayıcının dokümantasyonuna göre yazıldı; ancak henüz sağlayıcıların kendi hata ayıklama araçlarıyla (GA4 DebugView, Tag Assistant, Meta Pixel Helper, TikTok Pixel Helper) uçtan uca doğrulanmadı. Güvenmeden önce kendi kimliklerinizle bunu yapın; denetim listesi kısadır.
Sizin yerinize ne yapar
- Onaydan önce hiçbir şey yüklenmez. Varsayılan olarak bir sağlayıcının betiği, stub'ı ve çerezleri, ziyaretçi o sağlayıcının ihtiyaç duyduğu kategoriye izin verene kadar sayfaya girmez. Bu sırada izlenen olaylar boyutu ve yaşı sınırlı bir kuyrukta bekler, ret gelirse atılır.
- Çift sayım yok. Sayfa görüntülemelerinin tek sahibi vardır, ortak etiketlerde yönlendirme açıktır ve sayfa genelindeki kayıt aynı kimliğin iki kez başlatılmasını reddeder.
- Her yerde tek olay kimliği. Her olay, tüm sağlayıcıların tekilleştirme alanına aynı kimliği taşır; böylece aynı kimlikle sunucudan gönderilen kopya (Conversions API vb.) bir kez sayılır.
- Kişisel veri olay parametresinden sızamaz. Kullanıcı ayrı bir kimlik kanalından gider; çekirdek bu kanalı, hiçbir adaptör görmeden önce onaya göre süzer.
- Hatalı veri gürültülüdür. Geçersiz olay adı ya da tutar bütün hedefler için düşürülür ve bildirilir, katı kipte hata fırlatır. Hiçbir şey sessizce kırpılmaz.
- Yalnızca içe aktardığınız sağlayıcıyı paketlersiniz. Her sağlayıcı ayrı bir giriş noktasıdır.
- SSR güvenli. Sunucuda
createAnalytics()aynı yüzeye sahip etkisiz bir örnek döner.
Kurulum
npm install vue-analytics-kit
Vue 3.5 ya da üstü gerekir. vue-router ve firebase isteğe bağlı eş bağımlılıklardır; yalnızca yönlendirici entegrasyonu ve Firebase sağlayıcısı için gerekir.
Hızlı başlangıç
vue-router ile Vite SPA
// src/analytics.ts
import { createAnalytics } from 'vue-analytics-kit';
import { cookieConsentStorage } from 'vue-analytics-kit/consent';
import { ga4 } from 'vue-analytics-kit/providers/ga4';
import { meta } from 'vue-analytics-kit/providers/meta';
export const analytics = createAnalytics({
mode: import.meta.env.PROD ? 'live' : 'dry-run',
strict: import.meta.env.DEV,
consent: { storage: cookieConsentStorage(), policyVersion: '2026-10' },
providers: [
ga4({ measurementId: import.meta.env.VITE_GA4_ID, pageViews: 'core' }),
meta({ pixelId: import.meta.env.VITE_META_PIXEL_ID }),
],
});
// src/main.ts
import { trackRouter } from 'vue-analytics-kit/vue-router';
import { analytics } from './analytics';
const app = createApp(App).use(router).use(analytics);
trackRouter(analytics, router, { exclude: [/^\/reset-password/] });
app.mount('#app');
Inertia ile Laravel
// resources/js/analytics.ts
import { createAnalytics } from 'vue-analytics-kit';
import { cookieConsentStorage } from 'vue-analytics-kit/consent';
export const analytics = createAnalytics({
mode: import.meta.env.PROD ? 'live' : 'dry-run',
autoStart: false, // kimlikler ilk sayfayla gelir
consent: { storage: cookieConsentStorage() },
scripts: {
nonce: () => document.querySelector<HTMLMetaElement>('meta[name="csp-nonce"]')?.content,
},
});
// resources/js/app.ts
import { router } from '@inertiajs/vue3';
import { trackInertia } from 'vue-analytics-kit/inertia';
import { ga4 } from 'vue-analytics-kit/providers/ga4';
import { meta } from 'vue-analytics-kit/providers/meta';
import { analytics } from './analytics';
createInertiaApp({
withApp(app, { ssr, page }) {
app.use(analytics); // SSR sırasında etkisiz
if (ssr) return;
const ids = page.props.tracking as { ga4?: string; meta?: string };
analytics.start({
providers: [
ids.ga4 && ga4({ measurementId: ids.ga4, pageViews: 'core' }),
ids.meta && meta({ pixelId: ids.meta }),
],
});
trackInertia(analytics, router, { initialPage: page });
},
});
Bileşenlerde
<script setup lang="ts">
import { useAnalytics } from 'vue-analytics-kit';
const analytics = useAnalytics();
</script>
<template>
<button @click="analytics.track('sign_up', { method: 'email' })">Kayıt ol</button>
<button v-track="'navbar_help_clicked'">Yardım</button>
<button v-track="{ event: 'plan_selected', params: { name: plan.name } }">Seç</button>
</template>
v-track[:tetikleyici][.once][.capture] argüman olarak DOM olayını alır (varsayılan click). Bileşen dışında (store, yönlendirici guard'ı, HTTP geri çağrısı) kendi analytics örneğinizi doğrudan içe aktarın.
Sağlayıcılar
| Sağlayıcı | İçe aktarma | Gereken onay | Sağlayıcıda adı olmayan olaylar |
|---|---|---|---|
| GA4 | vue-analytics-kit/providers/ga4 |
analytics |
özel olay olarak gider |
| Google Tag Manager | vue-analytics-kit/providers/gtm |
siz bildirirsiniz | data layer'a itilir |
| Google Ads | vue-analytics-kit/providers/google-ads |
advertising |
yalnızca listelenen dönüşümler |
| Meta Pixel | vue-analytics-kit/providers/meta |
advertising |
açılmadıkça düşürülür |
| TikTok Pixel | vue-analytics-kit/providers/tiktok |
advertising |
açılmadıkça düşürülür |
| Firebase Analytics | vue-analytics-kit/providers/firebase |
analytics |
özel olay olarak gider |
ga4({ measurementId: 'G-XXXXXXXXXX', pageViews: 'core' });
gtm({ containerId: 'GTM-XXXXXXX', requires: ['analytics', 'advertising'], pageViews: 'core' });
googleAds({
conversionId: 'AW-XXXXXXXXX',
conversions: {
purchase: 'AbC-D_efG',
generate_lead: { label: 'hIjK', value: 50, currency: 'TRY' },
},
});
meta({ pixelId: '1234567890', advancedMatching: true });
tiktok({ pixelId: 'CXXXXXXXXXXXXXXXXXXX' });
firebase({
analytics: () => initializeAnalytics(app, { config: { send_page_view: false } }),
measurementId: 'G-XXXXXXXXXX',
pageViews: 'core',
});
Her sağlayıcı ayrıca şunları kabul eder:
{
alias: 'eu', // 'ga4:eu' olarak adreslenir
consentMode: 'gate' | 'signal', // bkz. Onay
events: { quiz_done: 'CompleteRegistration', // yeniden adlandır ya da sağlayıcının standart olayına yükselt
debug_ping: false }, // hiç gönderme
unmapped: 'custom' | 'drop' | ((name) => boolean),
beforeSend: (call, event) => call, // sağlayıcı çağrısına son bakış; null düşürür
trigger: 'immediate' | 'idle' | 'interaction' | 'manual',
}
Reklam pikselleri adı olmayan olayları neden varsayılan olarak düşürür. Gerçek bir uygulamadaki olayların çoğu arayüz tıklamasıdır. Hepsini bir reklam ağına göndermek veri minimizasyonu sorunudur ve o ağın olay yöneticisinde gürültü yaratır; TikTok özel olaylar üzerinden optimizasyon da yapamaz. Olay bazında events ile, topluca unmapped: 'custom' ile açın.
Bir ölçüm kimliği için GA4 ya da Firebase kullanın, ikisini birden değil. Firebase Analytics, aynı data layer üzerindeki GA4'tür; paket ikincisini reddeder.
Sayfa başına tek Tag Manager kapsayıcısı. Her kapsayıcı aynı data layer'ı okur, bu yüzden ikincisi reddedilir. Bir kapsayıcı zaten bir sağlayıcının etiketini ateşliyorsa o sağlayıcıyı ayrıca doğrudan eklemeyin; siz coexistsWith içinde listeleyene kadar paket bu birliktelik için uyarır.
Olaylar
Adlar GA4 kuralına uyar: küçük harfli snake_case, harfle başlar, en fazla 40 karakter. Desteklenen sağlayıcılar içinde en katı kural budur; geçerli bir ad her yerde geçerlidir.
Standart olaylar tip denetiminden geçer ve sağlayıcı başına çevrilir:
view_item_list view_item add_to_cart remove_from_cart add_to_wishlist view_cart begin_checkout add_shipping_info add_payment_info purchase search sign_up login generate_lead contact subscribe start_trial schedule submit_application view_content
Öğeler GA4 biçimindedir (item_id zorunlu). Bir value her zaman currency ile birlikte gider.
Başka her ad, düz parametreli serbest bir olaydır:
analytics.track('navbar_login_clicked', { name: 'header' });
Çalışma zamanında parçalardan ad üretmek için toEventName('odeme', 'Başarılı') → odeme_basarili. Türkçe karakterleri çevirir ama asla kısaltmaz: aynı 40 karaktere kırpılan iki uzun ad bütün raporlarda geri dönüşsüz biçimde birleşirdi; bu yüzden fazla uzun ad reddedilir.
Hatalı girdiye ne olur
| Girdi | Sonuç |
|---|---|
| Geçersiz, ayrılmış ya da fazla uzun ad | bütün hedefler için düşürülür, bildirilir |
currency olmadan value, NaN, "199,90" |
olay bütün hedefler için düşürülür, bildirilir |
"199.90" (PHP'nin serileştirdiği gibi ondalık dizgi) |
199.9 sayısına çevrilir |
| Parametre olarak nesne ya da dizi | o parametre çıkarılır, olay yine gider |
user_id, email, phone ya da e-posta biçimli bir değer |
o parametre çıkarılır: kimlik identify() üzerinden gider |
strict: true iken bunlar bir AnalyticsError fırlatır; geliştirmede istediğiniz budur.
Sözlüğü genişletmek
declare module 'vue-analytics-kit' {
interface AnalyticsEvents {
quiz_completed: { score: number; level: string };
}
// İsteğe bağlı: yalnızca yukarıda bildirilen adlara izin ver.
interface AnalyticsConfig {
strictEvents: true;
}
}
Çağrı başına seçenekler
analytics.track('purchase', payload, {
only: ['ga4', 'meta:1234567890'], // ya da except: [...]
eventId: 'purchase.ORD-1042', // sunucu taraflı göndericinizle paylaşın
once: true, // bu kimlikle tarayıcı oturumu başına en çok bir kez
});
track() olay kimliğini döner; hiçbir şey gönderilmediyse ''.
Onay
Paket bir onay bandı getirmez. Durumu ve anahtarları verir:
<script setup lang="ts">
import { useConsent } from 'vue-analytics-kit';
const consent = useConsent();
</script>
<template>
<aside v-if="consent.status.value === 'pending'">
<button @click="consent.grantAll()">Tümünü kabul et</button>
<button @click="consent.set({ analytics: 'granted', advertising: 'denied' })">
Yalnızca analitik
</button>
<button @click="consent.denyAll()">Reddet</button>
</aside>
</template>
Kategoriler: analytics, advertising, adUserData, adPersonalization, functional. İki ad… kategorisi siz ayarlamadıkça advertising'i izler; böylece üç anahtarlı bir bant da doğru Google Consent Mode v2 sinyalleri üretir. İlk karar, anmadığı her kategoriyi denied olarak çözer.
useConsent().status, sunucuda ve bileşen mount olana kadar 'unresolved' değerindedir; yukarıdaki bant bu sayede hydration uyuşmazlığına yol açamaz.
| Kategori | Google (GA4, Ads, GTM, Firebase) | Meta | TikTok |
|---|---|---|---|
analytics |
analytics_storage |
— | — |
advertising |
ad_storage |
consent grant / revoke |
grantConsent / revokeConsent |
adUserData |
ad_user_data |
gelişmiş eşleştirmeyi açar | identify'ı açar |
adPersonalization |
ad_personalization |
— | — |
functional |
functionality_storage, personalization_storage |
— | — |
Sağlayıcı başına iki kip:
gate(varsayılan): kategorileri verilene kadar sağlayıcıdan hiçbir şey sayfaya dokunmaz.signal: sağlayıcı yüklenir ve durum kendisine bildirilir: Google'ın gelişmiş Consent Mode'u, Meta'nın init öncesi revoke'u, TikTok'unholdConsent'i.
Karardan önceki olaylar bellekte bekler (100 olay, 30 dakika; ikisi de ayarlanabilir). Kuyruk dolduğunda dönüşümler ve sayfa görüntülemeleri arayüz olaylarından uzun yaşar. İzin gelirse sırayla gönderilir, ret gelirse atılır.
Kalıcılık sizin seçiminizdir. cookieConsentStorage() kararı birinci taraf bir çerezde tutar; backend'iniz de okuyabilir. Laravel'de bu çerezi çerez şifrelemesinin dışında tutun. Herkese yeniden sormak için policyVersion'ı değiştirin.
Onayı geri çekmek her sağlayıcıya bildirilir, iletim durur ve kullanıcı verisi temizlenir. Yüklenmiş betikler ve yazılmış çerezler bir sonraki sayfa yüklemesine kadar kalır; sayfayı yeniden yüklemek isterseniz analytics.on('consent', …) dinleyin.
Sayfa görüntülemeleri
analytics.page() bir sayfa görüntülemesi gönderir; yönlendirici entegrasyonları bunu sizin yerinize çağırır. En son iletilen sayfanın tekrarı yok sayılır; böylece çift dinleyici, hot reload ve Inertia'nın ilk sayfa için ateşlediği olay iki kez sayılamaz.
Her sağlayıcının otomatik sayfa izleme anlayışı ayrıdır; çift sayımın ana kaynağı budur:
| Sağlayıcı | Paket ne yapar | Sizin yapmanız gereken |
|---|---|---|
| Meta | pikselin geçmiş dinleyicisini kapatır | hiçbir şey |
| TikTok | pikselin geçmiş gözlemcisini kapatır | hiçbir şey |
| GA4, Firebase | send_page_view: false gönderir (GA4) ve kendi görüntülemesini atar |
pageViews seçin; 'core' ile GA4 mülkünde "Tarayıcı geçmişi olaylarına dayalı sayfa değişiklikleri"ni kapatın |
| Google Tag Manager | bir page_view olayı iter |
pageViews seçin; 'core' ile History Change tetikli sayfa görüntüleme etiketi olmasın |
GA4, Firebase ve Tag Manager için pageViews'in varsayılanı yoktur ve kod onsuz derlenmez: GA4'ün geçmişe dayalı sayfa görüntülemeleri yalnızca mülkün yönetim ekranından kapatılabilir, bu yüzden hangi varsayılan seçilse birileri için sessizce yanlış olurdu.
URL'ler herhangi bir yere gitmeden önce temizlenir: email, token, password ve signature sorgu değerleri ile e-posta biçimli değerler karartılır. Kuralı page: { sanitizeUrl } ile değiştirin. Meta ve TikTok location'ı kendileri okur; onlar için tek denetim sayfayı hiç bildirmemektir: yönlendirici entegrasyonunda exclude edin.
Kimlik
analytics.identify({
id: 'user-42', // kalıcı birinci taraf kimlik, asla e-posta değil
traits: { plan: 'pro' }, // analitik kullanıcı özellikleri
userData: { email: 'jane@example.com', phone: '+905551112233' },
});
analytics.reset(); // çıkışta
- Kimlik ve özellikler, bir hedefe kendi kategorisi verili olduğu sürece gider.
userData(gelişmiş eşleştirme, gelişmiş dönüşümler) ek olarakadUserDataiznini ve sağlayıcıdaadvancedMatching: trueister.- Değerler ham, kırpılmış ve küçük harfli iletilir: her sağlayıcının SDK'sı kendi usulüyle normalleştirir ve karmalar.
Meta eşleştirme verisini piksel başlatılırken alır, TikTok'a ise bir kez söylenebilir; ikisine de unutturulamaz. Bu yüzden çıkış, kullanıcı değişimi ya da adUserData'nın geri çekilmesinden sonra paket, bir yabancının olaylarını önceki kullanıcıya yazmak yerine, veri yeniden izinli olana ya da sayfa yeniden yüklenene kadar o piksele göndermeyi durdurur. Çıkış işleminiz sayfayı yeniden yüklemiyorsa bu piksellerin bir sonraki tam yüklemeye kadar sessiz kalmasını bekleyin.
Sunucu onaylı olaylar
Bir satın alma, ancak backend'iniz onayladığında gerçektir. Tarayıcıya flash'leyin; paket onu, backend'inizin sağlayıcıların sunucu API'lerine de gönderdiği kimlikle, oturum başına en çok bir kez iletir:
// Laravel, ödeme onaylandıktan sonra
Inertia::flash('analytics', [[
'id' => 'purchase.'.$order->number,
'name' => 'purchase',
'params' => [
'transaction_id' => $order->number,
'value' => $order->total, // ondalık dizgi olabilir
'currency' => 'TRY',
'items' => [['item_id' => $order->package_code]],
],
'ts' => now()->timestamp,
]]);
trackInertia() bunu ilk sayfadan ve sonraki gezinmelerden alır. Paylaşılan prop değil, flash verisi kullanın: Inertia prop'ları geçmiş durumunda tutar ve geri/ileri gezinmede yeniden oynatırdı.
Inertia yoksa aynı diziyi analytics.serverEvents.consume(payload) çağrısına verin.
Tarayıcı tarafı en-çok-bir-kez çalışır: kimlik gönderimden önce kaydedilir, o anda kapanan bir sekme tarayıcı kopyasını kaybeder. Teslimi güvenilir kılan, aynı kimlikle sunucudan yaptığınız gönderimdir.
Middleware ve kancalar
createAnalytics({
middleware: [
(event, ctx) => {
event.params.app_version = '2.4.0';
if (event.name.startsWith('debug_')) return null; // düşür
if (event.name === 'purchase') ctx.except('tiktok');
},
],
});
analytics.on('dispatch', (record) => {
// { envelope, destination, outcome: 'sent' | 'queued' | 'dropped' | 'simulated', reason?, call? }
});
analytics.on('diagnostic', (diagnostic) => {});
analytics.on('error', (error, context) => {});
Middleware eşzamanlıdır ve dağıtımdan önce olay başına bir kez çalışır. Hata fırlatan middleware olayı düşürür, çünkü redaksiyonu yapan o olabilir.
Kipler ve hata ayıklama
mode |
Davranış |
|---|---|
live |
gönderir |
dry-run |
bütün hattı çalıştırır, betik yüklemez, hiçbir şey göndermez, her dağıtımı yazar |
off |
hiçbir şey yapmaz |
Paket asla NODE_ENV okumaz: kipi açıkça verin. debug: true, live kipte de her dağıtım kaydını yazar.
Content Security Policy
Betikler nonce'unuzla (scripts.nonce) ve sayfa zorunlu kılıyorsa vue-analytics-kit adlı bir Trusted Types politikası üzerinden eklenir. Her sağlayıcı tanımı ihtiyaç duyduğu origin'leri listeler:
ga4({ measurementId: 'G-X', pageViews: 'core' }).origins;
// { script: ['https://www.googletagmanager.com'], connect: ['https://*.google-analytics.com', …], img: […] }
Yüklenemeyen bir betik (engellenmiş, çevrimdışı, zaman aşımı) uygulamanıza asla hata fırlatmaz: hedef olay almayı bırakır, betik gelirse sürdürür.
Sağlayıcı yazmak
Sağlayıcı, ne Vue'yu ne de çekirdeği içe aktaran vue-analytics-kit/adapter üzerine kurulu bir fabrikadır:
import {
defineProvider,
resolveEventName,
type CommonProviderOptions,
} from 'vue-analytics-kit/adapter';
export const acme = defineProvider<'acme', CommonProviderOptions & { siteId: string }>(
(options) => ({
key: 'acme',
id: options.siteId,
requires: ['analytics'],
consentMode: options.consentMode ?? 'gate',
pageViews: 'core',
capabilities: {
consentSignal: false,
heldEvents: 'core',
pageUrl: 'explicit',
dedup: 'none',
userData: false,
},
// Saf: kanonik olay girer, sağlayıcı çağrısı çıkar. window erişimi yok.
map(event) {
if (event.type === 'page')
return { name: 'pageview', kind: 'page', params: { url: event.page.url } };
const resolved = resolveEventName(event.name, {}, options, 'custom');
return resolved && { ...resolved, params: { ...event.params } };
},
// Onay izin verdiğinde, tarayıcıda, bir kez çalışır: stub, init, betik.
setup(ctx) {
const queue = ((ctx.window as any).acme ??= []);
return {
script: ctx.loadScript(`https://cdn.acme.example/${options.siteId}.js`),
send: (call) => queue.push([call.name, call.params]),
};
},
}),
);
Çekirdek şunları garanti eder: setup canlı hedef başına en çok bir kez çalışır ve SSR ya da dry-run sırasında hiç çalışmaz; send, track() sırasıyla ve yalnızca onay izin verdiği sürece çağrılır; kimlik onaya göre süzülmüş olarak gelir.
Yayına almadan önce
- Sitenizi gerçek kimliklerle ve onay verilmiş hâlde açın. GA4 DebugView, Tag Assistant, Meta Pixel Helper ve TikTok Pixel Helper'da her eylemin bir kez göründüğünü denetleyin.
- Birkaç sayfa arasında gezinin ve her sayfa görüntülemesinin bir kez sayıldığını denetleyin. GA4 iki gösteriyorsa mülkün geçmişe dayalı sayfa görüntülemeleri hâlâ açıktır.
- Yeni bir ziyarette onayı reddedin ve ağ sekmesine bakın: hiçbir sağlayıcıya istek gitmemeli.
- Üretim Content Security Policy'niz altında konsolu denetleyin.
- Bir sunucu API'siyle tekilleştiriyorsanız bir test satın alması gönderin ve sağlayıcının bir kez raporladığını denetleyin.
Sürümleme
Sürüm 0.x iken küçük (minor) sürüm kırıcı değişiklik içerebilir, yama (patch) sürümü asla içermez. 1.0'ın donduracağı şeyler olay sözlüğü ve adaptör sözleşmesidir.
Lisans
Related Packages
Consent-gated tag manager for Nuxt, built on @nuxt/scripts: loads the tags publi...
Meta (Facebook) Pixel module for Nuxt 3 and 4. Auto-imported composable, per-rou...
Consent-first analytics for the browser and server: one API in front of GA4, Met...
Version History
| Version | Released | Vue | License |
|---|---|---|---|
| 0.1.0 | ^3.5.0 | MIT |