Exemplo de Integração Vue
Este guia fornece um exemplo completo e pronto para produção de integração do Zenovay analytics em uma aplicação Vue 3 usando a Composition API e padrões modernos do Vue.
Início Rápido
Adicione o script de rastreamento do Zenovay ao seu 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>
Pronto! O script rastreia automaticamente as visualizações de página ao carregar.
Configuração Básica
1. Criar um Composable Zenovay
Crie um composable reutilizável para rastreamento em todo o seu aplicativo 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. Rastrear Visualizações de Página (Vue Router)
Para aplicações de página única usando Vue Router, rastreie mudanças de navegação:
// 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. Rastrear Eventos Customizados
<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>
Exemplo Completo
Aqui está uma implementação completa com todos os casos de uso comuns:
Composable para Rastreamento de Página
// 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 }
);
}
Componente Botão Rastreável
<!-- 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>
Exemplo de Uso
<!-- 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>
Padrões Avançados
Rastreamento Condicional
Rastreie eventos apenas quando certas condições forem atendidas:
<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 para Rastreamento de Formulário
// 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
};
}
Componente de Formulário
<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 para Rastreamento de E-commerce
// 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
};
}
Exemplo de Página de E-commerce
<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 Rastreamento de Erros
// 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
});
}
};
}
}
};
Identificação de Usuário
<!-- 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>
Diretiva Global para Rastreamento de Cliques
// 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);
}
});
}
});
}
};
Uso com Diretiva
<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>
Suporte TypeScript
Suporte completo a TypeScript com eventos tipados:
// 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,
};
}
Integração com Pinia Store
Integre analytics com o gerenciamento de estado 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,
};
});
Otimização de Performance
Rastreamento com Debounce
<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>
Testes
Mockar Zenovay em Testes
// test/setup.js
// Mock the global zenovay object before tests
beforeEach(() => {
window.zenovay = vi.fn();
});
Teste de Componente
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' });
});
});
Integração Nuxt 3
Integração completa com 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);
}
});
Rastreamento de Página 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>
Configuração 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',
},
],
},
},
});
Alternativamente, se preferir não usar o plugin Nuxt, você pode adicionar o script diretamente à configuração head do seu nuxt.config.ts conforme mostrado acima. Ambas as abordagens funcionam igualmente bem.
Variáveis de Ambiente
# .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).