Saltar al contenido principal
10 min de lectura

Ejemplo de Integración de Vue

Esta guía proporciona un ejemplo completo y listo para producción de integración de análisis de Zenovay en una aplicación Vue 3 usando la Composition API y patrones modernos de Vue.

Inicio Rápido

Agrega el script de seguimiento de Zenovay a tu 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>

Eso es todo para el seguimiento básico de visualización de páginas. El script rastrea automáticamente las vistas de página al cargar.

Configuración Básica

1. Crear un Composable de Zenovay

Crea un composable reutilizable para el seguimiento en toda tu aplicación 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 Vistas de Página (Vue Router)

Para aplicaciones de una sola página que usan Vue Router, rastrea los cambios de navegación:

// 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 Personalizados

<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>

Ejemplo Completo

Aquí hay una implementación completa con todos los casos de uso comunes:

Composable para Seguimiento de Páginas

// 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 de Botón Rastreable

<!-- 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>

Ejemplo 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>

Patrones Avanzados

Seguimiento Condicional

Rastrea eventos solo cuando se cumplan ciertas condiciones:

<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 Rastreo de Formularios

// 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 Formulario

<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 Rastreo de Comercio Electrónico

// 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
  };
}

Ejemplo de Página de Comercio Electrónico

<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 Rastreo de Errores

// 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
          });
        }
      };
    }
  }
};

Identificación de Usuario

<!-- 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>

Directiva Global para Rastreo de 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);
          }
        });
      }
    });
  }
};

Uso con Directiva

<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>

Compatibilidad con TypeScript

Compatibilidad completa con TypeScript con 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,
  };
}

Integración con Pinia Store

Integra análisis con gestión 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,
  };
});

Optimización del Rendimiento

Rastreo con 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>

Pruebas

Mock de Zenovay en Pruebas

// test/setup.js
// Mock the global zenovay object before tests
beforeEach(() => {
  window.zenovay = vi.fn();
});

Prueba 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' });
  });
});

Integración con Nuxt 3

Integración completa con 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);
  }
});

Rastreo de Páginas de 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>

Configuración 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',
        },
      ],
    },
  },
});

Alternativamente, si prefieres no usar el plugin de Nuxt, puedes agregar el script directamente a la configuración de encabezado de tu nuxt.config.ts como se muestra arriba. Ambos enfoques funcionan igual de bien.

Variables de Entorno

# .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).

Recursos Adicionales

¿Fue útil esta página?