Exemple d'intégration Vue
Ce guide fournit un exemple complet et prêt pour la production d'intégration des analyses Zenovay dans une application Vue 3 en utilisant l'API Composition et les modèles Vue modernes.
Démarrage rapide
Ajoutez le script de suivi Zenovay à votre index.html :
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>My Vue App</title>
</head>
<body>
<div id="app"></div>
<script defer data-tracking-code="YOUR_TRACKING_CODE" src="https://api.zenovay.com/z.js"></script>
<script type="module" src="/src/main.js"></script>
</body>
</html>
C'est tout ce qu'il faut pour le suivi basique des pages vues. Le script suit automatiquement les pages vues lors du chargement.
Configuration de base
1. Créer un composable Zenovay
Créez un composable réutilisable pour le suivi dans votre application Vue :
// composables/useZenovay.js
export function useZenovay() {
const track = (eventName, eventData = {}) => {
if (window.zenovay) {
window.zenovay('track', eventName, eventData);
}
};
const identify = (userId, traits = {}) => {
if (window.zenovay) {
window.zenovay('identify', userId, traits);
}
};
const trackGoal = (goalName, data = {}) => {
if (window.zenovay) {
window.zenovay('goal', goalName, data);
}
};
const trackPurchase = (data) => {
if (window.zenovay) {
window.zenovay('revenue', data.amount, data.currency);
}
};
return {
track,
identify,
trackGoal,
trackPurchase,
};
}
2. Suivre les pages vues (Vue Router)
Pour les applications monopages utilisant Vue Router, suivez les changements de navigation :
// router/index.js
import { createRouter, createWebHistory } from 'vue-router';
const router = createRouter({
history: createWebHistory(),
routes: [
// your routes
]
});
router.afterEach((to) => {
// The Zenovay script automatically tracks the initial page load.
// For SPA route changes, you can trigger a page view manually:
if (window.zenovay) {
window.zenovay('track', 'pageview', {
path: to.fullPath,
title: document.title,
});
}
});
export default router;
3. Suivre les événements personnalisés
<script setup>
import { useZenovay } from '@/composables/useZenovay';
const { track } = useZenovay();
const handleSignup = async () => {
track('signup_started', {
plan: 'professional',
source: 'pricing_page',
});
// Continue with signup logic
};
</script>
<template>
<button @click="handleSignup">Start Free Trial</button>
</template>
Exemple complet
Voici une implémentation complète avec tous les cas d'utilisation courants :
Composable pour le suivi des pages
// composables/usePageTracking.js
import { watch } from 'vue';
import { useRoute } from 'vue-router';
export function usePageTracking() {
const route = useRoute();
watch(
() => route.fullPath,
() => {
if (window.zenovay) {
window.zenovay('track', 'pageview', {
path: route.fullPath,
referrer: document.referrer,
title: document.title,
});
}
},
{ immediate: true }
);
}
Composant bouton suivable
<!-- components/TrackableButton.vue -->
<script setup>
import { useZenovay } from '@/composables/useZenovay';
const props = defineProps({
eventName: {
type: String,
required: true
},
eventData: {
type: Object,
default: () => ({})
}
});
const emit = defineEmits(['click']);
const { track } = useZenovay();
const handleClick = (event) => {
// Track the event
track(props.eventName, props.eventData);
// Emit click event for parent handling
emit('click', event);
};
</script>
<template>
<button @click="handleClick">
<slot />
</button>
</template>
Exemple d'utilisation
<!-- pages/Pricing.vue -->
<script setup>
import TrackableButton from '@/components/TrackableButton.vue';
</script>
<template>
<div>
<h1>Pricing</h1>
<TrackableButton
event-name="plan_selected"
:event-data="{
plan: 'scale',
billing: 'monthly',
price: 90
}"
class="btn-primary"
>
Choose Scale
</TrackableButton>
</div>
</template>
Modèles avancés
Suivi conditionnel
Suivez les événements uniquement lorsque certaines conditions sont remplies :
<script setup>
import { watchEffect } from 'vue';
import { useZenovay } from '@/composables/useZenovay';
const props = defineProps(['product']);
const { track } = useZenovay();
watchEffect(() => {
// Only track if product is high-value
if (props.product && props.product.price > 1000) {
track('high_value_product_viewed', {
product_id: props.product.id,
product_name: props.product.name,
price: props.product.price
});
}
});
</script>
<template>
<div><!-- product details --></div>
</template>
Composable de suivi de formulaire
// composables/useFormTracking.js
import { ref } from 'vue';
import { useZenovay } from '@/composables/useZenovay';
export function useFormTracking(formId) {
const { track } = useZenovay();
const formData = ref({});
const trackFieldFocus = (fieldName) => {
track('form_field_focused', {
form_id: formId,
field_name: fieldName
});
};
const trackFormSubmit = (success = true) => {
track('form_submitted', {
form_id: formId,
success,
field_count: Object.keys(formData.value).length
});
};
return {
formData,
trackFieldFocus,
trackFormSubmit
};
}
Composant formulaire
<script setup>
import { useFormTracking } from '@/composables/useFormTracking';
const { formData, trackFieldFocus, trackFormSubmit } = useFormTracking('contact');
const handleSubmit = async () => {
try {
// Submit form logic
await submitContactForm(formData.value);
trackFormSubmit(true);
} catch (error) {
trackFormSubmit(false);
}
};
</script>
<template>
<form @submit.prevent="handleSubmit">
<input
v-model="formData.name"
type="text"
placeholder="Name"
@focus="trackFieldFocus('name')"
/>
<input
v-model="formData.email"
type="email"
placeholder="Email"
@focus="trackFieldFocus('email')"
/>
<textarea
v-model="formData.message"
placeholder="Message"
@focus="trackFieldFocus('message')"
/>
<button type="submit">Send Message</button>
</form>
</template>
Composable de suivi du commerce électronique
// composables/useEcommerceTracking.js
import { useZenovay } from '@/composables/useZenovay';
export function useEcommerceTracking() {
const { track, trackPurchase } = useZenovay();
const trackProductView = (product) => {
track('product_viewed', {
product_id: product.id,
product_name: product.name,
category: product.category,
price: product.price,
currency: 'USD'
});
};
const trackAddToCart = (product, quantity = 1) => {
track('product_added_to_cart', {
product_id: product.id,
product_name: product.name,
quantity,
price: product.price,
total: product.price * quantity
});
};
const trackRemoveFromCart = (product) => {
track('product_removed_from_cart', {
product_id: product.id,
product_name: product.name
});
};
const trackCheckout = (cart) => {
track('checkout_started', {
cart_total: cart.total,
item_count: cart.items.length,
currency: 'USD'
});
};
const trackPurchaseCompleted = (order) => {
trackPurchase({
amount: order.total,
currency: 'USD',
product: `Order #${order.id}`,
});
// Also track a detailed custom event
track('purchase_completed', {
order_id: order.id,
revenue: order.total,
tax: order.tax,
shipping: order.shipping,
currency: 'USD',
items: order.items.map(item => ({
product_id: item.id,
quantity: item.quantity,
price: item.price
}))
});
};
return {
trackProductView,
trackAddToCart,
trackRemoveFromCart,
trackCheckout,
trackPurchaseCompleted
};
}
Exemple de page de commerce électronique
<script setup>
import { onMounted } from 'vue';
import { useEcommerceTracking } from '@/composables/useEcommerceTracking';
const props = defineProps(['product']);
const {
trackProductView,
trackAddToCart
} = useEcommerceTracking();
onMounted(() => {
trackProductView(props.product);
});
const handleAddToCart = () => {
trackAddToCart(props.product);
// Add to cart logic
};
</script>
<template>
<div>
<h1>{{ product.name }}</h1>
<p>${{ product.price }}</p>
<button @click="handleAddToCart">Add to Cart</button>
</div>
</template>
Plugin de suivi des erreurs
// plugins/errorTracking.js
export default {
install(app) {
// Global error handler
app.config.errorHandler = (error, instance, info) => {
if (window.zenovay) {
window.zenovay('track', 'error_occurred', {
error_message: error.message,
error_stack: error.stack,
component_name: instance?.$options?.name,
lifecycle_hook: info,
error_type: 'vue_error'
});
}
console.error('Vue error:', error, info);
};
// Global warning handler (development only)
if (import.meta.env.DEV) {
app.config.warnHandler = (msg, instance, trace) => {
if (window.zenovay) {
window.zenovay('track', 'warning_occurred', {
warning_message: msg,
component_name: instance?.$options?.name,
component_trace: trace
});
}
};
}
}
};
Identification de l'utilisateur
<!-- components/UserIdentifier.vue -->
<script setup>
import { watch } from 'vue';
import { useZenovay } from '@/composables/useZenovay';
import { useAuthStore } from '@/stores/auth';
const { identify } = useZenovay();
const authStore = useAuthStore();
watch(
() => authStore.user,
(user) => {
if (user) {
identify(user.id, {
email: user.email,
name: user.name,
plan: user.subscription?.plan,
signup_date: user.createdAt
});
}
},
{ immediate: true }
);
</script>
<template>
<!-- This component doesn't render anything -->
<div style="display: none" />
</template>
Directive globale pour le suivi des clics
// directives/track.js
export default {
install(app) {
app.directive('track', {
mounted(el, binding) {
el.addEventListener('click', () => {
const [eventName, eventData] = Array.isArray(binding.value)
? binding.value
: [binding.value, {}];
if (window.zenovay) {
window.zenovay('track', eventName, eventData);
}
});
}
});
}
};
Utilisation avec la directive
<template>
<!-- Simple event tracking -->
<button v-track="'button_clicked'">
Click Me
</button>
<!-- With event data -->
<button v-track="['plan_selected', { plan: 'professional' }]">
Choose Plan
</button>
</template>
Support TypeScript
Support TypeScript complet avec événements typés :
// types/analytics.ts
export interface AnalyticsEvents {
signup_started: {
plan: 'free' | 'pro' | 'scale' | 'enterprise';
source: string;
};
product_viewed: {
product_id: string;
product_name: string;
price: number;
};
purchase_completed: {
order_id: string;
revenue: number;
currency: string;
};
}
// composables/useZenovay.ts
// Declare the global zenovay object for TypeScript
declare global {
interface Window {
zenovay: (...args: any[]) => void;
}
}
import type { AnalyticsEvents } from '@/types/analytics';
export function useZenovay() {
const track = <K extends keyof AnalyticsEvents>(
eventName: K,
data: AnalyticsEvents[K]
) => {
if (window.zenovay) {
window.zenovay('track', eventName, data);
}
};
const identify = (userId: string, traits: Record<string, any> = {}) => {
if (window.zenovay) {
window.zenovay('identify', userId, traits);
}
};
const trackGoal = (goalName: string, data: Record<string, any> = {}) => {
if (window.zenovay) {
window.zenovay('goal', goalName, data);
}
};
return {
track,
identify,
trackGoal,
};
}
Intégration du magasin Pinia
Intégrez l'analyse avec la gestion d'état Pinia :
// stores/analytics.js
import { defineStore } from 'pinia';
export const useAnalyticsStore = defineStore('analytics', () => {
const track = (eventName, eventData = {}) => {
try {
if (window.zenovay) {
window.zenovay('track', eventName, eventData);
}
} catch (error) {
console.error('Analytics tracking error:', error);
}
};
const identify = (userId, traits = {}) => {
if (window.zenovay) {
window.zenovay('identify', userId, traits);
}
};
return {
track,
identify,
};
});
Optimisation des performances
Suivi avec délai
<script setup>
import { ref } from 'vue';
import { useDebounceFn } from '@vueuse/core';
import { useZenovay } from '@/composables/useZenovay';
const searchQuery = ref('');
const { track } = useZenovay();
const trackSearch = useDebounceFn((query) => {
track('search_performed', {
query,
length: query.length
});
}, 500);
const handleSearch = (event) => {
searchQuery.value = event.target.value;
trackSearch(searchQuery.value);
};
</script>
<template>
<input
v-model="searchQuery"
type="search"
placeholder="Search..."
@input="handleSearch"
/>
</template>
Tests
Simuler Zenovay dans les tests
// test/setup.js
// Mock the global zenovay object before tests
beforeEach(() => {
window.zenovay = vi.fn();
});
Test de composant
import { mount } from '@vue/test-utils';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import TrackableButton from '@/components/TrackableButton.vue';
describe('TrackableButton', () => {
beforeEach(() => {
window.zenovay = vi.fn();
});
it('tracks event on click', async () => {
const wrapper = mount(TrackableButton, {
props: {
eventName: 'button_clicked',
eventData: { id: '123' }
},
slots: {
default: 'Click Me'
}
});
await wrapper.find('button').trigger('click');
expect(window.zenovay).toHaveBeenCalledWith('track', 'button_clicked', { id: '123' });
});
});
Intégration Nuxt 3
Intégration complète avec Nuxt 3 :
// plugins/zenovay.client.js
import { defineNuxtPlugin } from '#app';
export default defineNuxtPlugin(() => {
// Add the tracking script dynamically
if (typeof document !== 'undefined') {
const script = document.createElement('script');
script.defer = true;
script.dataset.trackingCode = 'YOUR_TRACKING_CODE';
script.src = 'https://api.zenovay.com/z.js';
document.head.appendChild(script);
}
});
Suivi des pages Nuxt
<!-- app.vue -->
<script setup>
import { watch } from 'vue';
import { useRoute } from 'vue-router';
const route = useRoute();
watch(
() => route.fullPath,
() => {
if (window.zenovay) {
window.zenovay('track', 'pageview', {
path: route.fullPath,
title: document.title,
});
}
},
{ immediate: true }
);
</script>
<template>
<NuxtPage />
</template>
Configuration de Nuxt
// nuxt.config.ts
export default defineNuxtConfig({
app: {
head: {
script: [
{
defer: true,
'data-tracking-code': 'YOUR_TRACKING_CODE',
src: 'https://api.zenovay.com/z.js',
},
],
},
},
});
Vous pouvez également ajouter le script directement à la configuration head de votre nuxt.config.ts comme indiqué ci-dessus si vous préférez ne pas utiliser le plugin Nuxt. Les deux approches fonctionnent également bien.
Variables d'environnement
# .env
# No special environment variables needed for Zenovay!
# The tracking code is embedded directly in the script tag.
# Simply replace YOUR_TRACKING_CODE with your actual tracking code
# from the Zenovay dashboard (app.zenovay.com).