Pular para o conteúdo principal
11 min de leitura

Frameworks personalizados

Integre o Zenovay analytics com frameworks personalizados, ferramentas de build e arquiteturas únicas. Este guia cobre padrões para qualquer configuração não contemplada pelas nossas integrações oficiais.


Referência rápida

Tipo de integraçãoComplexidadeMelhor para
Tag de scriptSimplesSites estáticos, configurações básicas
API JavaScriptMédiaSPAs, roteamento dinâmico
API HTTPAvançadoServidor, headless, IoT
Proxy próprioAvançadoBypass de bloqueadores de anúncios

Métodos principais de integração

Método 1: Tag de script (O mais simples)

Adicione o script de rastreamento a qualquer página HTML:

<!DOCTYPE html>
<html>
<head>
    <title>Seu Site</title>
    <!-- Zenovay Analytics -->
    <script defer data-tracking-code="YOUR_TRACKING_CODE" src="https://api.zenovay.com/z.js"></script>
</head>
<body>
    <!-- Seu conteúdo -->
</body>
</html>

Atributos:

AtributoObrigatórioDescrição
data-tracking-codeSimSeu código de rastreamento
deferRecomendadoCarregamento não bloqueante
data-api-urlNãoURL do proxy próprio
data-autoNãoDefina como false para desativar as visualizações de página automáticas

Método 2: API JavaScript

Para controle programático:

// Função de fila (funciona antes do carregamento do script)
window.zenovay = window.zenovay || function() {
    (window.zenovay.q = window.zenovay.q || []).push(arguments);
};

// Rastrear visualizações de página manualmente
zenovay('page');

// Rastrear eventos personalizados
zenovay('track', 'button_click', {
    button_name: 'signup',
    location: 'header'
});

// Identificar usuários
zenovay('identify', 'user_123', {
    email: '[email protected]',
    plan: 'pro'
});

Método 3: API HTTP

Para ambientes no servidor ou fora do navegador:

// Exemplo Node.js
const response = await fetch('https://api.zenovay.com/e/YOUR_TRACKING_CODE', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'X-Forwarded-For': clientIp // Importante para geolocalização
    },
    body: JSON.stringify({
        event: 'pageview',
        url: 'https://yoursite.com/page',
        referrer: 'https://google.com',
        user_agent: clientUserAgent,
        timestamp: new Date().toISOString()
    })
});

Veja Endpoints da API para a documentação completa da API.


Aplicações de página única (SPAs)

As SPAs requerem rastreamento manual de visualizações de página nas mudanças de rota.

Padrão genérico para SPA

// Criar um wrapper de rastreamento
class ZenovayTracker {
    constructor(siteId) {
        this.siteId = siteId;
        this.previousPath = null;
    }

    init() {
        // Função de fila (funciona antes do carregamento do script)
        window.zenovay = window.zenovay || function() {
            (window.zenovay.q = window.zenovay.q || []).push(arguments);
        };
    }

    trackPageView(path) {
        // Evitar rastreamento duplicado
        if (path === this.previousPath) return;
        this.previousPath = path;

        zenovay('page');
    }

    trackEvent(name, properties = {}) {
        zenovay('track', name, properties);
    }
}

// Uso
const tracker = new ZenovayTracker('YOUR_TRACKING_CODE');
tracker.init();

// Na mudança de rota
router.on('change', (route) => {
    tracker.trackPageView(route.path);
});

Integração com a History API

Para SPAs que usam a History API:

// Interceptar pushState e replaceState
const originalPushState = history.pushState;
const originalReplaceState = history.replaceState;

history.pushState = function(...args) {
    originalPushState.apply(this, args);
    window.dispatchEvent(new Event('locationchange'));
};

history.replaceState = function(...args) {
    originalReplaceState.apply(this, args);
    window.dispatchEvent(new Event('locationchange'));
};

// Também tratar popstate (voltar/avançar)
window.addEventListener('popstate', () => {
    window.dispatchEvent(new Event('locationchange'));
});

// Rastrear na mudança de localização
window.addEventListener('locationchange', () => {
    zenovay('page');
});

Roteamento baseado em hash

Para apps que usam roteamento por hash:

window.addEventListener('hashchange', () => {
    zenovay('page');
});

// Visualização de página inicial
document.addEventListener('DOMContentLoaded', () => {
    zenovay('page');
});

Geradores de sites estáticos

Eleventy (11ty)

Crie _includes/analytics.njk:

<!-- Zenovay Analytics -->
<script defer data-tracking-code="{{ site.zenovayId }}" src="https://api.zenovay.com/z.js"></script>

Adicione ao seu template base:

<!DOCTYPE html>
<html>
<head>
    {% include "analytics.njk" %}
</head>
<body>
    {{ content | safe }}
</body>
</html>

Em _data/site.js:

module.exports = {
    zenovayId: process.env.ZENOVAY_TRACKING_CODE || 'YOUR_TRACKING_CODE'
};

Hugo

Crie layouts/partials/analytics.html:

{{ if not .Site.IsServer }}
<script defer data-tracking-code="{{ .Site.Params.zenovayId }}" src="https://api.zenovay.com/z.js"></script>
{{ end }}

Em config.toml:

[params]
zenovayId = "YOUR_TRACKING_CODE"

Inclua em layouts/_default/baseof.html:

<head>
    {{ partial "analytics.html" . }}
</head>

Jekyll

Crie _includes/analytics.html:

{% if jekyll.environment == "production" %}
<script defer data-tracking-code="{{ site.zenovay_id }}" src="https://api.zenovay.com/z.js"></script>
{% endif %}

Em _config.yml:

zenovay_id: "YOUR_TRACKING_CODE"

Gatsby

Crie gatsby-ssr.js:

import React from "react";

export const onRenderBody = ({ setHeadComponents }) => {
    if (process.env.NODE_ENV === "production") {
        setHeadComponents([
            <script
                key="zenovay"
                defer
                data-tracking-code={process.env.GATSBY_ZENOVAY_ID}
                src="https://api.zenovay.com/z.js"
            />
        ]);
    }
};

Para mudanças de rota, em gatsby-browser.js:

export const onRouteUpdate = ({ location }) => {
    if (typeof window.zenovay !== 'undefined') {
        window.zenovay('page');
    }
};

Remix

Adicione ao app/root.tsx:

import { Scripts } from "@remix-run/react";

export default function App() {
    return (
        <html>
            <head>
                {process.env.NODE_ENV === "production" && (
                    <script
                        defer
                        data-tracking-code={process.env.ZENOVAY_ID}
                        src="https://api.zenovay.com/z.js"
                    />
                )}
            </head>
            <body>
                <Outlet />
                <Scripts />
            </body>
        </html>
    );
}

Progressive Web Apps (PWAs)

Rastreamento de instalação

// Rastrear instalação de PWA
window.addEventListener('beforeinstallprompt', (e) => {
    zenovay('track', 'pwa_install_prompt_shown');
});

window.addEventListener('appinstalled', () => {
    zenovay('track', 'pwa_installed', {
        timestamp: new Date().toISOString()
    });
});

Fila de rastreamento offline

Rastreie eventos offline e envie quando voltar online:

class OfflineQueue {
    constructor() {
        this.storageKey = 'zenovay_offline_queue';
        this.init();
    }

    init() {
        // Enviar eventos enfileirados ao ficar online
        window.addEventListener('online', () => this.flush());

        // Verificar se precisa enviar ao carregar
        if (navigator.onLine) {
            this.flush();
        }
    }

    getQueue() {
        try {
            return JSON.parse(localStorage.getItem(this.storageKey)) || [];
        } catch {
            return [];
        }
    }

    saveQueue(queue) {
        localStorage.setItem(this.storageKey, JSON.stringify(queue));
    }

    add(event, properties) {
        const queue = this.getQueue();
        queue.push({
            event,
            properties,
            timestamp: new Date().toISOString(),
            url: window.location.href
        });
        this.saveQueue(queue);

        if (navigator.onLine) {
            this.flush();
        }
    }

    async flush() {
        const queue = this.getQueue();
        if (queue.length === 0) return;

        const successful = [];

        for (const item of queue) {
            try {
                zenovay('track', item.event, {
                    ...item.properties,
                    _queued_at: item.timestamp,
                    _sent_at: new Date().toISOString()
                });
                successful.push(item);
            } catch (error) {
                console.error('Failed to send queued event:', error);
                break; // Parar na primeira falha
            }
        }

        // Remover itens enviados com sucesso
        const remaining = queue.filter(item => !successful.includes(item));
        this.saveQueue(remaining);
    }
}

// Uso
const offlineQueue = new OfflineQueue();

// Rastrear evento (funciona offline)
offlineQueue.add('page_view', { path: '/home' });

Integração com Service Worker

No seu service worker:

// sw.js
self.addEventListener('sync', (event) => {
    if (event.tag === 'zenovay-sync') {
        event.waitUntil(syncAnalytics());
    }
});

async function syncAnalytics() {
    const cache = await caches.open('zenovay-analytics');
    const requests = await cache.keys();

    for (const request of requests) {
        try {
            await fetch(request);
            await cache.delete(request);
        } catch {
            // Tentará novamente na próxima sincronização
        }
    }
}

Web Components

Elemento personalizado com rastreamento

class TrackableButton extends HTMLElement {
    static get observedAttributes() {
        return ['track-event', 'track-properties'];
    }

    connectedCallback() {
        this.addEventListener('click', this.handleClick.bind(this));
    }

    handleClick() {
        const eventName = this.getAttribute('track-event') || 'button_click';
        let properties = {};

        try {
            properties = JSON.parse(this.getAttribute('track-properties') || '{}');
        } catch {}

        if (window.zenovay) {
            zenovay('track', eventName, {
                ...properties,
                element: this.tagName.toLowerCase(),
                text: this.textContent
            });
        }
    }
}

customElements.define('trackable-button', TrackableButton);

Uso:

<trackable-button
    track-event="cta_click"
    track-properties='{"location": "hero", "variant": "primary"}'>
    Começar
</trackable-button>

Considerações sobre Shadow DOM

Ao usar o Shadow DOM, anexe ouvintes de eventos ao shadow root:

class TrackedComponent extends HTMLElement {
    constructor() {
        super();
        this.attachShadow({ mode: 'open' });
    }

    connectedCallback() {
        this.render();
        this.setupTracking();
    }

    render() {
        // Criar elemento de botão de forma segura
        const button = document.createElement('button');
        button.id = 'cta';
        button.textContent = 'Clique aqui';
        this.shadowRoot.appendChild(button);
    }

    setupTracking() {
        // Rastrear cliques dentro do Shadow DOM
        this.shadowRoot.addEventListener('click', (e) => {
            if (e.target.id === 'cta') {
                zenovay('track', 'shadow_button_click', {
                    component: 'tracked-component'
                });
            }
        });

        // Rastrear visibilidade com Intersection Observer
        const observer = new IntersectionObserver((entries) => {
            entries.forEach(entry => {
                if (entry.isIntersecting) {
                    zenovay('track', 'component_viewed', {
                        component: 'tracked-component'
                    });
                    observer.disconnect();
                }
            });
        });

        observer.observe(this);
    }
}

Integrações com ferramentas de build

Plugin Vite

Crie vite-plugin-zenovay.js:

export default function zenovayPlugin(options = {}) {
    const { siteId, enabled = true } = options;

    return {
        name: 'vite-plugin-zenovay',
        transformIndexHtml(html) {
            if (!enabled || !siteId) return html;

            const script = `<script defer data-tracking-code="${siteId}" src="https://api.zenovay.com/z.js"></script>`;

            return html.replace('</head>', `${script}</head>`);
        }
    };
}

// Uso em vite.config.js
import zenovayPlugin from './vite-plugin-zenovay';

export default {
    plugins: [
        zenovayPlugin({
            siteId: process.env.VITE_ZENOVAY_ID,
            enabled: process.env.NODE_ENV === 'production'
        })
    ]
};

Plugin Webpack

Crie webpack-zenovay-plugin.js:

const HtmlWebpackPlugin = require('html-webpack-plugin');

class ZenovayWebpackPlugin {
    constructor(options = {}) {
        this.options = options;
    }

    apply(compiler) {
        compiler.hooks.compilation.tap('ZenovayPlugin', (compilation) => {
            HtmlWebpackPlugin.getHooks(compilation).beforeEmit.tapAsync(
                'ZenovayPlugin',
                (data, cb) => {
                    if (this.options.siteId && this.options.enabled !== false) {
                        const script = `<script defer data-tracking-code="${this.options.siteId}" src="https://api.zenovay.com/z.js"></script>`;
                        data.html = data.html.replace('</head>', `${script}</head>`);
                    }
                    cb(null, data);
                }
            );
        });
    }
}

module.exports = ZenovayWebpackPlugin;

// Uso em webpack.config.js
const ZenovayPlugin = require('./webpack-zenovay-plugin');

module.exports = {
    plugins: [
        new HtmlWebpackPlugin(),
        new ZenovayPlugin({
            siteId: process.env.ZENOVAY_ID,
            enabled: process.env.NODE_ENV === 'production'
        })
    ]
};

Plugin Rollup

// rollup-plugin-zenovay.js
export default function zenovayPlugin(options = {}) {
    return {
        name: 'zenovay',
        generateBundle(outputOptions, bundle) {
            for (const fileName in bundle) {
                if (fileName.endsWith('.html')) {
                    const chunk = bundle[fileName];
                    if (chunk.type === 'asset') {
                        const script = `<script defer data-tracking-code="${options.siteId}" src="https://api.zenovay.com/z.js"></script>`;
                        chunk.source = chunk.source.replace('</head>', `${script}</head>`);
                    }
                }
            }
        }
    };
}

Rastreamento no servidor

Padrão de SDK Node.js

// zenovay-server.js
class ZenovayServer {
    constructor(siteId, options = {}) {
        this.siteId = siteId;
        this.apiUrl = options.apiUrl || 'https://api.zenovay.com/e/';
        this.queue = [];
        this.flushInterval = options.flushInterval || 5000;
        this.batchSize = options.batchSize || 10;

        this.startFlushTimer();
    }

    startFlushTimer() {
        setInterval(() => this.flush(), this.flushInterval);
    }

    track(event, properties = {}, context = {}) {
        this.queue.push({
            event,
            properties,
            user_agent: context.userAgent,
            ip: context.ip,
            url: context.url,
            referrer: context.referrer,
            timestamp: new Date().toISOString()
        });

        if (this.queue.length >= this.batchSize) {
            this.flush();
        }
    }

    async flush() {
        if (this.queue.length === 0) return;

        const events = this.queue.splice(0, this.batchSize);

        try {
            await fetch(this.apiUrl + this.siteId, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ events })
            });
        } catch (error) {
            // Reenfileirar em caso de falha
            this.queue.unshift(...events);
            console.error('Zenovay: Failed to send events', error);
        }
    }

    // Middleware Express
    middleware() {
        return (req, res, next) => {
            req.trackEvent = (event, properties) => {
                this.track(event, properties, {
                    userAgent: req.headers['user-agent'],
                    ip: req.ip,
                    url: req.originalUrl,
                    referrer: req.headers.referer
                });
            };
            next();
        };
    }
}

module.exports = ZenovayServer;

// Uso
const ZenovayServer = require('./zenovay-server');
const zenovay = new ZenovayServer('YOUR_TRACKING_CODE');

app.use(zenovay.middleware());

app.get('/checkout', (req, res) => {
    req.trackEvent('checkout_started', {
        cart_value: 99.99
    });
    res.render('checkout');
});

Exemplo em Python

import requests
from datetime import datetime
from threading import Thread
from queue import Queue

class ZenovayTracker:
    def __init__(self, tracking_code, api_url='https://api.zenovay.com/e/'):
        self.tracking_code = tracking_code
        self.api_url = api_url
        self.queue = Queue()
        self._start_worker()

    def _start_worker(self):
        def worker():
            while True:
                event = self.queue.get()
                try:
                    requests.post(self.api_url + self.tracking_code, json=event, timeout=5)
                except Exception as e:
                    print(f"Zenovay error: {e}")
                self.queue.task_done()

        thread = Thread(target=worker, daemon=True)
        thread.start()

    def track(self, event, properties=None, context=None):
        context = context or {}
        self.queue.put({
            'event': event,
            'properties': properties or {},
            'user_agent': context.get('user_agent'),
            'ip': context.get('ip'),
            'url': context.get('url'),
            'timestamp': datetime.utcnow().isoformat()
        })

# Exemplo com Flask
from flask import Flask, request

app = Flask(__name__)
tracker = ZenovayTracker('YOUR_TRACKING_CODE')

@app.route('/purchase', methods=['POST'])
def purchase():
    tracker.track('purchase', {
        'revenue': request.json.get('total'),
        'currency': 'USD'
    }, {
        'user_agent': request.headers.get('User-Agent'),
        'ip': request.remote_addr,
        'url': request.url
    })
    return {'status': 'ok'}

Testes e depuração

Modo debug

// Ativar modo debug
zenovay('debug');

// Todos os eventos serão registrados no console
zenovay('track', 'test_event', { test: true });
// Console: [Zenovay] Event tracked: test_event { test: true }

Rastreador mock para testes

// __mocks__/zenovay.js
const mockZenovay = {
    calls: [],

    handler(...args) {
        mockZenovay.calls.push(args);
    },

    reset() {
        mockZenovay.calls = [];
    },

    getEvents() {
        return mockZenovay.calls.filter(c => c[0] === 'track');
    }
};

window.zenovay = (...args) => mockZenovay.handler(...args);
window.zenovay.mock = mockZenovay;

// Em seu teste
test('tracks button click', () => {
    window.zenovay.mock.reset();

    fireEvent.click(getByRole('button', { name: 'Sign Up' }));

    expect(window.zenovay.mock.getEvents()).toContainEqual(
        ['track', 'signup_click', { location: 'header' }]
    );
});

Inspeção de rede

Verifique se o rastreamento está funcionando:

  1. Abra o DevTools → aba Network
  2. Filtre por zenovay ou z.js
  3. Clique pelo seu site
  4. Confirme as requisições para api.zenovay.com

Política de segurança de conteúdo

Configure os cabeçalhos CSP para permitir o Zenovay:

Content-Security-Policy:
    default-src 'self';
    script-src 'self' https://api.zenovay.com;
    connect-src 'self' https://api.zenovay.com;

Para rastreamento próprio (recomendado):

Content-Security-Policy:
    default-src 'self';
    script-src 'self';
    connect-src 'self';

Boas práticas

  1. Carregue de forma assíncrona: sempre use defer ou async para evitar bloqueios
  2. Trate erros: encapsule as chamadas de rastreamento em try-catch para maior resiliência
  3. Respeite a privacidade: verifique o consentimento antes de rastrear, respeite o DNT
  4. Agrupe eventos: combine eventos quando possível para reduzir requisições
  5. Teste rigorosamente: verifique o rastreamento em múltiplos navegadores e dispositivos
  6. Use rastreamento próprio: configure o rastreamento próprio para maior precisão
  7. Monitore o desempenho: certifique-se de que o rastreamento não impacta os Core Web Vitals

Recursos relacionados


Precisa de ajuda com sua integração? Entre em contato com [email protected].

Esta página foi útil?