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
| Integrationstyp | Komplexität | Geeignet für |
|---|---|---|
| Script-Tag | Einfach | Statische Seiten, grundlegende Setups |
| JavaScript-API | Mittel | SPAs, dynamisches Routing |
| HTTP-API | Fortgeschritten | Serverseitig, headless, IoT |
| First-Party-Proxy | Fortgeschritten | Ad-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:
| Attribut | Erforderlich | Beschreibung |
|---|---|---|
data-tracking-code | Ja | Ihr Tracking-Code |
defer | Empfohlen | Nicht-blockierendes Laden |
data-api-url | Nein | First-Party-Proxy-URL |
data-auto | Nein | Auf 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:
- Öffnen Sie DevTools → Netzwerk-Tab
- Filtern Sie nach
zenovayoderz.js - Klicken Sie auf Ihrer Seite herum
- Ü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
- Asynchron laden: Verwenden Sie immer
deferoderasync, um Blockierungen zu vermeiden - Fehler behandeln: Umschließen Sie Tracking-Aufrufe mit try-catch für Stabilität
- Datenschutz respektieren: Überprüfen Sie die Einwilligung vor dem Tracking, beachten Sie DNT
- Ereignisse bündeln: Kombinieren Sie Ereignisse wenn möglich, um Anfragen zu reduzieren
- Gründlich testen: Überprüfen Sie Tracking in mehreren Browsern und Geräten
- First-Party verwenden: Richten Sie First-Party-Tracking für bessere Genauigkeit ein
- Leistung überwachen: Stellen Sie sicher, dass Tracking keine Core Web Vitals beeinträchtigt
Verwandte Ressourcen
- Tracking-Skript
- Benutzerdefinierte Ereignisse
- First-Party-Tracking
- API-Endpunkte
- Vanilla JS-Beispiel
Benötigen Sie Hilfe bei Ihrer Integration? Kontaktieren Sie [email protected].