Zum Hauptinhalt springen
11 Min. Lesedauer

Benutzerdefinierte Frameworks

Integrieren Sie Zenovay Analytics in benutzerdefinierte Frameworks, Build-Tools und einzigartige Architekturen. Dieser Leitfaden behandelt Muster für jedes Setup, das nicht durch unsere offiziellen Integrationen abgedeckt ist.


Kurzübersicht

IntegrationstypKomplexitätGeeignet für
Script-TagEinfachStatische Seiten, grundlegende Setups
JavaScript-APIMittelSPAs, dynamisches Routing
HTTP-APIFortgeschrittenServerseitig, headless, IoT
First-Party-ProxyFortgeschrittenAd-Blocker-Umgehung

Kern-Integrationsmethoden

Methode 1: Script-Tag (Einfachste Methode)

Fügen Sie das Tracking-Skript zu jeder HTML-Seite hinzu:

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

Attribute:

AttributErforderlichBeschreibung
data-tracking-codeJaIhr Tracking-Code
deferEmpfohlenNicht-blockierendes Laden
data-api-urlNeinFirst-Party-Proxy-URL
data-autoNeinAuf false setzen, um automatische Seitenaufrufe zu deaktivieren

Methode 2: JavaScript-API

Für programmatische Kontrolle:

// Queue function (works before script loads)
window.zenovay = window.zenovay || function() {
    (window.zenovay.q = window.zenovay.q || []).push(arguments);
};

// Track page views manually
zenovay('page');

// Track custom events
zenovay('track', 'button_click', {
    button_name: 'signup',
    location: 'header'
});

// Identify users
zenovay('identify', 'user_123', {
    email: '[email protected]',
    plan: 'pro'
});

Methode 3: HTTP-API

Für server-seitige oder Nicht-Browser-Umgebungen:

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

Weitere Informationen finden Sie in der API-Endpunkte-Dokumentation.


Single Page Applications (SPAs)

SPAs erfordern manuelles Seitenaufruf-Tracking bei Routenänderungen.

Allgemeines SPA-Muster

// Create a tracking wrapper
class ZenovayTracker {
    constructor(siteId) {
        this.siteId = siteId;
        this.previousPath = null;
    }

    init() {
        // Queue function (works before script loads)
        window.zenovay = window.zenovay || function() {
            (window.zenovay.q = window.zenovay.q || []).push(arguments);
        };
    }

    trackPageView(path) {
        // Prevent duplicate tracking
        if (path === this.previousPath) return;
        this.previousPath = path;

        zenovay('page');
    }

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

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

// On route change
router.on('change', (route) => {
    tracker.trackPageView(route.path);
});

History-API-Integration

Für SPAs, die die History API verwenden:

// Intercept pushState and 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'));
};

// Also handle popstate (back/forward)
window.addEventListener('popstate', () => {
    window.dispatchEvent(new Event('locationchange'));
});

// Track on location change
window.addEventListener('locationchange', () => {
    zenovay('page');
});

Hash-basiertes Routing

Für Apps mit Hash-Routing:

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

// Initial page view
document.addEventListener('DOMContentLoaded', () => {
    zenovay('page');
});

Static Site Generators

Eleventy (11ty)

Erstellen Sie _includes/analytics.njk:

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

Fügen Sie es zu Ihrer Basis-Vorlage hinzu:

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

In _data/site.js:

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

Hugo

Erstellen Sie 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 }}

In config.toml:

[params]
zenovayId = "YOUR_TRACKING_CODE"

In layouts/_default/baseof.html einbinden:

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

Jekyll

Erstellen Sie _includes/analytics.html:

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

In _config.yml:

zenovay_id: "YOUR_TRACKING_CODE"

Gatsby

Erstellen Sie 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"
            />
        ]);
    }
};

Für Routenänderungen in gatsby-browser.js:

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

Remix

Fügen Sie zu app/root.tsx hinzu:

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)

Installations-Tracking

// Track PWA installation
window.addEventListener('beforeinstallprompt', (e) => {
    zenovay('track', 'pwa_install_prompt_shown');
});

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

Offline-Tracking-Warteschlange

Ereignisse offline verfolgen, beim Online-Gehen senden:

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

    init() {
        // Send queued events when coming online
        window.addEventListener('online', () => this.flush());

        // Check if we need to flush on load
        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; // Stop on first failure
            }
        }

        // Remove successfully sent items
        const remaining = queue.filter(item => !successful.includes(item));
        this.saveQueue(remaining);
    }
}

// Usage
const offlineQueue = new OfflineQueue();

// Track event (works offline)
offlineQueue.add('page_view', { path: '/home' });

Service-Worker-Integration

In Ihrem 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 {
            // Will retry on next sync
        }
    }
}

Web Components

Benutzerdefiniertes Element mit Tracking

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

Verwendung:

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

Shadow DOM-Überlegungen

Bei Verwendung von Shadow DOM fügen Sie Ereignis-Listener an den Shadow Root an:

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

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

    render() {
        // Create button element safely
        const button = document.createElement('button');
        button.id = 'cta';
        button.textContent = 'Click Me';
        this.shadowRoot.appendChild(button);
    }

    setupTracking() {
        // Track clicks within shadow DOM
        this.shadowRoot.addEventListener('click', (e) => {
            if (e.target.id === 'cta') {
                zenovay('track', 'shadow_button_click', {
                    component: 'tracked-component'
                });
            }
        });

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

Build-Tool-Integrationen

Vite-Plugin

Erstellen Sie 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>`);
        }
    };
}

// Usage in 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'
        })
    ]
};

Webpack-Plugin

Erstellen Sie 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;

// Usage in 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'
        })
    ]
};

Rollup-Plugin

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

Serverseitiges Tracking

Node.js-SDK-Muster

// 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) {
            // Re-queue on failure
            this.queue.unshift(...events);
            console.error('Zenovay: Failed to send events', error);
        }
    }

    // Express middleware
    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;

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

Python-Beispiel

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()
        })

# Flask example
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'}

Tests und Debugging

Debug-Modus

// Enable debug mode
zenovay('debug');

// All events will be logged to console
zenovay('track', 'test_event', { test: true });
// Console: [Zenovay] Event tracked: test_event { test: true }

Mock-Tracker für Tests

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

// In your test
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' }]
    );
});

Netzwerküberprüfung

Überprüfen Sie, ob Tracking funktioniert:

  1. Öffnen Sie DevTools → Netzwerk-Tab
  2. Filtern Sie nach zenovay oder z.js
  3. Klicken Sie auf Ihrer Seite herum
  4. Überprüfen Sie Anfragen an api.zenovay.com

Content Security Policy

Konfigurieren Sie CSP-Header, um Zenovay zu erlauben:

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

Für First-Party-Tracking (empfohlen):

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

Best Practices

  1. Asynchron laden: Verwenden Sie immer defer oder async, um Blockierungen zu vermeiden
  2. Fehler behandeln: Umschließen Sie Tracking-Aufrufe mit try-catch für Stabilität
  3. Datenschutz respektieren: Überprüfen Sie die Einwilligung vor dem Tracking, beachten Sie DNT
  4. Ereignisse bündeln: Kombinieren Sie Ereignisse wenn möglich, um Anfragen zu reduzieren
  5. Gründlich testen: Überprüfen Sie Tracking in mehreren Browsern und Geräten
  6. First-Party verwenden: Richten Sie First-Party-Tracking für bessere Genauigkeit ein
  7. Leistung überwachen: Stellen Sie sicher, dass Tracking keine Core Web Vitals beeinträchtigt

Verwandte Ressourcen


Benötigen Sie Hilfe bei Ihrer Integration? Kontaktieren Sie [email protected].

War diese Seite hilfreich?