Zum Inhalt springen

JavaScript SDK – Repository-Leitfaden

Über das Braze JavaScript SDK

Das Braze JavaScript SDK hilft Ihnen, Braze-Messaging-, Analytics- und Nutzer:innen-Engagement-Funktionen in Ihre Anwendung zu integrieren.

Für den Einstieg nutzen Sie die folgenden Ressourcen:

Architektur-Übersicht

Das Braze JavaScript SDK ist eine plattformunabhängige Bibliothek, die in jeder reinen JavaScript-Umgebung funktioniert. Es enthält keine browser- oder Node.js-spezifischen APIs, was es für den Einsatz in verschiedenen JavaScript-Laufzeitumgebungen geeignet macht.

Zentrale Design-Prinzipien:

  • Dependency Injection: Das SDK erfordert Implementierungen für Speicher, Netzwerk und Geräteinformationen, anstatt plattformspezifische APIs zu verwenden
  • Async-First: Die meisten API-Methoden sind asynchron und geben Promises zurück; einige Hilfsmethoden (z. B. destroy, subscribeToInAppMessage, toggleLogging, setLogger) sind synchron. Die genauen Signaturen finden Sie in den TypeScript-Definitionen.
  • Singleton-Session: Die API auf Modulebene (initialize/destroy) verwaltet jeweils eine aktive SDK-Session.
  • Internes Dependency-Management: Erstellt und verwaltet interne Abhängigkeiten (UserManager, SessionManager, DataFlushController usw.) auf Basis der bereitgestellten Implementierungen

Schnellstart

Installieren Sie das SDK mit npm:

npm install @braze/javascript-sdk

Oder mit yarn:

yarn add @braze/javascript-sdk

Mit der API auf Modulebene:

import { initialize, openSession, changeUser } from '@braze/javascript-sdk';

await initialize({
  apiKey,
  baseUrl,
  options,
  sdkMetadata,
  deviceInfo,
  storageManager,
  networkManager,
});

await changeUser(userId);

await openSession();

Voraussetzungen

Bevor Sie das Braze JavaScript SDK integrieren, benötigen Sie Folgendes:

  • Braze-Konto: Ein Braze-Konto mit API-Zugang
  • API-Schlüssel: Den API-Schlüssel Ihrer App aus dem Braze-Dashboard
  • SDK-Endpunkt: Die URL Ihres Braze SDK-Endpunkts (z. B. sdk.iad-01.braze.com)

Ihre Zugangsdaten abrufen

  1. API-Schlüssel: Zu finden in Ihrem Braze-Dashboard unter Einstellungen > API-Schlüssel
  2. SDK-Endpunkt: Zu finden unter Einstellungen > SDK-Authentifizierung > Endpunkte

Integration

API aufrufen

Verwenden Sie die API auf Modulebene: Rufen Sie initialize() einmal auf und verwenden Sie dann die exportierten Funktionen. Um die Konfiguration zu wechseln, rufen Sie zuerst destroy() und dann erneut initialize() auf.

import { initialize, logPurchase, changeUser } from '@braze/javascript-sdk';

await initialize({ apiKey, baseUrl, options, ... });
await changeUser('user-123');
await logPurchase('sku-1', 9.99, 'USD', 1);

Kernkonzepte

Erforderliche Implementierungen

Das Konfigurationsobjekt für die Initialisierung erfordert storageManager. networkManager und pushManager sind optional.

1. StorageManager – Asynchrone Schlüssel-Wert-Speicherschnittstelle

interface StorageManager {
  store(key: string, value: string, isId?: boolean): Promise<void>;
  remove(key: string, isId?: boolean): Promise<void>;
  retrieve(key: string, isId?: boolean): Promise<string | null>;
  clearData(storageKeys: string[]): Promise<void>;
}
  • Der Parameter isId kennzeichnet persistente ID-Speicherung: Wenn true, speichert das SDK einen persistenten Bezeichner (Geräte-ID, Nutzer-ID) oder das Opt-out-Flag. Implementierungen sollten diese über App-Neustarts hinweg beibehalten, damit das SDK dasselbe Gerät bzw. dieselbe:n Nutzer:in erkennen kann. Wenn false, handelt es sich um Sitzungs-/Cache-Daten (Ereignisse, Attribute usw.), die nur im Arbeitsspeicher gehalten werden können. In Web-Umgebungen sollten Sie für Schlüssel, die mit isId: true gespeichert werden, Cookies verwenden, um sitzungsübergreifende Persistenz sicherzustellen.
  • Muss asynchrone Operationen für alle Speichervorgänge unterstützen

2. NetworkManager (optional) – Schnittstelle für HTTP-POST-Anfragen

interface NetworkManager {
  postRequest(
    url: string,
    data: Partial<Record<string, unknown>>,
    headers?: globalThis.Headers | [string, string][]
  ): Promise<Partial<Record<string, unknown>>>;
}
  • Die Standardimplementierung verwendet die fetch-API (erfordert globales fetch und URL)
  • Kann überschrieben werden, wenn fetch nicht die bevorzugte API ist
  • Hinweis: Das SDK verfügt bereits über eine integrierte Logik für Wiederholungsversuche und Rate-Limiting

3. PushManager (optional) – Schnittstelle für Push-Benachrichtigungen

interface PushManager {
  isPushBlocked(): boolean | undefined;
  isPushPermissionGranted(): boolean | undefined;
  isPushSupported(): boolean | undefined;
  registerPush(
    successCallback?: (endpoint: string, publicKey: string, userAuth: string) => void,
    deniedCallback?: (temporaryDenial: boolean) => void,
  ): void;
  unregisterPush(successCallback?: () => void, errorCallback?: () => void): void;
}
  • Nur erforderlich, wenn Push-Benachrichtigungen implementiert werden

Data-Flushing

Das SDK überträgt zwischengespeicherte Daten automatisch alle 10 Sekunden an die Braze-Server (konfigurierbar über flushIntervalInSeconds). Verwenden Sie requestImmediateDataFlush(), um eine sofortige Synchronisierung zu erzwingen.

Integrationsmuster

Für Methodensignaturen, Parameter- und Rückgabetypen sowie vollständige API-Details siehe die TypeScript-Definitionen im Paket.

Grundlegende Integration

Vollständiges, funktionsfähiges Beispiel mit Fehlerbehandlung:

import {
  initialize,
  openSession,
  changeUser,
  logCustomEvent,
  type StorageManager,
  type DeviceInfo
} from '@braze/javascript-sdk';

// Implement required StorageManager interface (in-memory only; does not persist data).
// This example treats all keys equally and ignores the isId parameter
// See "Complete StorageManager implementation with IndexedDB" below for an
// example where we properly handle isId
class InMemoryStorageManager implements StorageManager {
  private storage = new Map<string, string>();

  async store(key: string, value: string, isId?: boolean): Promise<void> {
    this.storage.set(key, value);
  }

  async retrieve(key: string, isId?: boolean): Promise<string | null> {
    return this.storage.get(key) ?? null;
  }

  async remove(key: string, isId?: boolean): Promise<void> {
    this.storage.delete(key);
  }

  async clearData(storageKeys: string[]): Promise<void> {
    for (const key of storageKeys) {
      this.storage.delete(key);
    }
  }
}

const storageManager: StorageManager = new InMemoryStorageManager();

// Provide device information (use your platform's APIs for non-browser)
const deviceInfo: DeviceInfo = {
  os: 'my-runtime-os',
  language: 'en',
  timezone: 'UTC',
  browser: 'Chrome', // Optional
  browserVersion: '120', // Optional
  userAgent: "some-user-agent" // Optional
};

// Browser-only example (uncomment and adapt if you are running in a web browser)
// const deviceInfo: DeviceInfo = {
//   os: navigator.platform || 'Unknown',
//   language: navigator.language || 'en',
//   timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
//   browser: 'Chrome', // Optional
//   browserVersion: '120', // Optional
//   userAgent: navigator.userAgent // Optional
// };

// Initialize SDK
try {
  const initialized = await initialize({
    apiKey: 'YOUR-API-KEY-HERE',
    baseUrl: 'sdk.iad-01.braze.com', // Your Braze SDK endpoint
    options: {
      sdkVersion: '1.0.0',
      enableLogging: true, // Remove in production
      sessionTimeoutInSeconds: 1800, // 30 minutes
      flushIntervalInSeconds: 10
    },
    sdkMetadata: ['npm'], // Identify your platform
    deviceInfo,
    storageManager
  });

  if (!initialized) {
    console.error('Failed to initialize Braze SDK');
    return;
  }

  // Identify user (wait for promise to resolve)
  await changeUser('user-123');

  // Open session (must be after changeUser)
  const isNewSession = await openSession();
  console.log('Session opened:', isNewSession ? 'new' : 'resumed');

  // Log events
  await logCustomEvent('app_opened', {
    source: 'homepage',
    timestamp: new Date().toISOString()
  });

} catch (error) {
  console.error('Braze SDK error:', error);
}

Angepasste Speicher-Implementierung

Vollständige StorageManager-Implementierung mit IndexedDB für persistente IDs:

import type { StorageManager } from '@braze/javascript-sdk';

class IndexedDBStorageManager implements StorageManager {
  private dbName = 'braze-storage';
  private storeName = 'braze-ids';
  private memoryCache = new Map<string, string>();
  private db: IDBDatabase | null = null;
  private dbInitPromise: Promise<void> | null = null;

  private async initDB(): Promise<void> {
    return new Promise((resolve, reject) => {
      const request = indexedDB.open(this.dbName, 1);
      request.onerror = () => reject(request.error);
      request.onsuccess = () => {
        this.db = request.result;
        resolve();
      };
      request.onupgradeneeded = (event) => {
        const db = (event.target as IDBOpenDBRequest).result;
        if (!db.objectStoreNames.contains(this.storeName)) {
          db.createObjectStore(this.storeName);
        }
      };
    });
  }

  private async ensureDB(): Promise<void> {
    if (this.dbInitPromise !== null) {
      return this.dbInitPromise;
    }
    this.dbInitPromise = this.initDB();
    return this.dbInitPromise;
  }

  async store(key: string, value: string, isId?: boolean): Promise<void> {
    await this.ensureDB();
    this.memoryCache.set(key, value);

    if (isId && this.db) {
      try {
        const transaction = this.db.transaction([this.storeName], 'readwrite');
        const store = transaction.objectStore(this.storeName);
        await new Promise<void>((resolve, reject) => {
          const request = store.put(value, key);
          request.onsuccess = () => resolve();
          request.onerror = () => reject(request.error);
        });
      } catch (error) {
        console.error('Failed to store ID in IndexedDB:', error);
      }
    }
  }

  async retrieve(key: string, isId?: boolean): Promise<string | null> {
    await this.ensureDB();
    if (this.memoryCache.has(key)) {
      return this.memoryCache.get(key) || null;
    }

    if (isId && this.db) {
      try {
        const transaction = this.db.transaction([this.storeName], 'readonly');
        const store = transaction.objectStore(this.storeName);
        return new Promise<string | null>((resolve, reject) => {
          const request = store.get(key);
          request.onsuccess = () => {
            const value = request.result;
            if (value) {
              this.memoryCache.set(key, value);
            }
            resolve(value || null);
          };
          request.onerror = () => reject(request.error);
        });
      } catch (error) {
        console.error('Failed to retrieve ID from IndexedDB:', error);
        return null;
      }
    }

    return null;
  }

  async remove(key: string, isId?: boolean): Promise<void> {
    await this.ensureDB();
    this.memoryCache.delete(key);

    if (isId && this.db) {
      try {
        const transaction = this.db.transaction([this.storeName], 'readwrite');
        const store = transaction.objectStore(this.storeName);
        await new Promise<void>((resolve, reject) => {
          const request = store.delete(key);
          request.onsuccess = () => resolve();
          request.onerror = () => reject(request.error);
        });
      } catch (error) {
        console.error('Failed to remove ID from IndexedDB:', error);
      }
    }
  }

  async clearData(storageKeys: string[]): Promise<void> {
    await this.ensureDB();
    for (const key of storageKeys) {
      this.memoryCache.delete(key);
    }

    if (this.db) {
      try {
        const transaction = this.db.transaction([this.storeName], 'readwrite');
        const store = transaction.objectStore(this.storeName);
        await Promise.all(
          storageKeys.map(
            (key) =>
              new Promise<void>((resolve, reject) => {
                const request = store.delete(key);
                request.onsuccess = () => resolve();
                request.onerror = () => reject(request.error);
              })
          )
        );
      } catch (error) {
        console.error('Failed to clear data from IndexedDB:', error);
      }
    }
  }
}

const storageManager = new IndexedDBStorageManager();

Angepasste Netzwerk-Implementierung

NetworkManager, der jede ausgehende Anfrage protokolliert (das SDK übernimmt bereits die Fehlerbehandlung und Wiederholungsversuche):

import type { NetworkManager } from '@braze/javascript-sdk';

function logRequest(url: string, data: Partial<Record<string, unknown>>): void {
  // Send to your analytics, monitoring, or logging backend
  console.log('Braze SDK request', { url, data });
}

class LoggingNetworkManager implements NetworkManager {
  async postRequest(
    url: string,
    data: Partial<Record<string, unknown>>,
    headers?: Headers | [string, string][]
  ): Promise<Partial<Record<string, unknown>>> {
    logRequest(url, data);

    const requestHeaders = new Headers(headers);
    requestHeaders.set('Content-Type', 'application/json');

    const response = await fetch(url, {
      method: 'POST',
      headers: requestHeaders,
      body: JSON.stringify(data),
    });

    const result = await response.json();
    return result as Partial<Record<string, unknown>>;
  }
}

const networkManager = new LoggingNetworkManager();

Fehlerbehandlung

Vollständige Muster zur Fehlerbehandlung:

import {
  getUserId,
  logCustomEvent,
  initialize,
} from '@braze/javascript-sdk';

// Pattern 1: Check for undefined (SDK not initialized)
async function getDevice() {
  const deviceId = await getDeviceId();
  if (deviceId === undefined) {
    console.warn('SDK not initialized');
    return null;
  }
  return deviceId;
}

// Pattern 2: Try-catch for methods that may throw
async function logEventSafely() {
  try {
    const success = await logCustomEvent('button_clicked', { button: 'submit' });
    if (success === undefined) {
      console.warn('SDK not initialized, event not logged');
    } else if (success) {
      console.log('Event logged successfully');
    } else {
      console.warn('Event failed to enqueue');
    }
  } catch (error) {
    console.error('Error logging event:', error);
    // Handle error (e.g., retry, queue for later)
  }
}

// Pattern 3: Handle null vs undefined distinction
async function checkUser() {
  const userId = await getUserId();

  if (userId === undefined) {
    // SDK not initialized
    console.warn('SDK not initialized');
  } else if (userId === null) {
    // Current user is anonymous
    console.log('Current user is anonymous');
  } else {
    // User is identified
    console.log(`User ID is ${userId}`);
  }
}

// Pattern 4: Handle initialization errors
async function initializeSafely() {
  try {
    const initialized = await initialize({
      apiKey: 'YOUR-API-KEY',
      baseUrl: 'sdk.iad-01.braze.com',
      options: { sdkVersion: '1.0.0' },
      sdkMetadata: ['npm'],
      deviceInfo: { os: 'iOS', language: 'en', timezone: 'UTC' },
      storageManager: myStorageManager
    });

    if (!initialized) {
      console.error('Failed to initialize SDK');
      // Check if already initialized, disabled, or validation failed
      return false;
    }

    return true;
  } catch (error) {
    console.error('Initialization error:', error);
    return false;
  }
}

Abo-Management

import {
  ControlMessage,
  logInAppMessageImpression,
  removeSubscription,
  subscribeToInAppMessage,
} from '@braze/javascript-sdk';

const displayMessage = (inAppMessage) => {
  // Add custom code to display in-app messages
}

// Subscribe to in-app messages
const subscriptionId = subscribeToInAppMessage((inAppMessage) => {
  if (inAppMessage instanceof ControlMessage) {
    return; // Skip control messages
  }

  displayMessage(inAppMessage);

  logInAppMessageImpression(inAppMessage);
});

// Later, remove subscription if it was successfully created
if (subscriptionId) {
  removeSubscription(subscriptionId);
}

Konfiguration wechseln: Es kann immer nur eine aktive Sitzung gleichzeitig existieren. Um die Konfiguration zu wechseln, rufen Sie destroy() und dann initialize() auf:

import { destroy, initialize } from '@braze/javascript-sdk';

destroy();
await initialize({ /* new config */ });

Häufige Anwendungsfälle

Nutzeridentifikation und Attribut-Tracking

import {
  changeUser,
  setCustomUserAttribute,
  setUserEmail,
  setUserFirstName,
  setUserLastName,
} from '@braze/javascript-sdk';

// Identify user
await changeUser('user-123');

// Set standard attributes
await setUserEmail('[email protected]');
await setUserFirstName('John');
await setUserLastName('Doe');

// Set custom attributes
await setCustomUserAttribute('subscription_tier', 'premium');
await setCustomUserAttribute('last_login', new Date());
await setCustomUserAttribute('tags', ['vip', 'early-adopter']);

Ereignis-Logging und Analytics

import {
  logCustomEvent,
  logPurchase,
  requestImmediateDataFlush,
} from '@braze/javascript-sdk';

await logCustomEvent('product_viewed', {
  product_id: '123',
  category: 'electronics',
  price: 99.99
});

await logPurchase('product-123', 99.99, 'USD', 1, {
  category: 'electronics'
});

// Flushing these events to the server will happen periodically,
// however you can manually trigger a flush if necessary
requestImmediateDataFlush((success) => {
  console.log('Data flushed:', success);
});

Handhabung von In-App Messages

import {
  ControlMessage,
  logInAppMessageImpression,
  subscribeToInAppMessage,
} from '@braze/javascript-sdk';

const displayInAppMessage = async (inAppMessage) => {
  // Add custom code to display in-app messages
}

subscribeToInAppMessage(async (inAppMessage) => {
  if (inAppMessage instanceof ControlMessage) {
    return;
  }

  await displayInAppMessage(inAppMessage);

  await logInAppMessageImpression(inAppMessage);
});

Fehlerbehandlung und Grenzfälle

Häufige Fehlerbedingungen

SDK nicht initialisiert:

  • Die meisten Methoden geben undefined zurück (statt zu werfen), wenn das SDK nicht initialisiert ist
  • initialize() gibt false zurück, wenn bereits initialisiert oder die Validierung fehlschlägt
  • changeUser() ist ein No-Op und das Promise wird aufgelöst, wenn das SDK nicht initialisiert ist
  • Prüfen Sie immer auf undefined-Rückgabewerte, bevor Sie sie verwenden

Validierungsfehler:

  • Ungültiger API-Schlüssel oder Base-URL: initialize() gibt false zurück und protokolliert einen Fehler
  • Ungültige Ereignisnamen/Schlüssel: Maximal 255 Zeichen, darf nicht mit $ beginnen, nur alphanumerische Zeichen und Satzzeichen
  • Ungültige Attributwerte: Strings maximal 255 Zeichen, keine Zeilenumbrüche/Tabs/doppelte Anführungszeichen, darf nicht mit $ beginnen
  • Ungültige Währungscodes: Nicht unterstützte Codes führen zu einer Warnung, es wird keine Aktion ausgeführt
  • Ungültige Kaufmenge: Muss zwischen 1 und 100 liegen, wird andernfalls ignoriert

Netzwerkfehler:

  • postRequest() des NetworkManagers sollte Fehler behandeln und Promises entsprechend ablehnen
  • Der Data-Flush-Controller wiederholt fehlgeschlagene Anfragen automatisch
  • Verwenden Sie den Callback von requestImmediateDataFlush(), um Flush-Fehler zu erkennen

Speicherfehler:

  • StorageManager-Methoden sollten Fehler ordnungsgemäß behandeln
  • Wenn die Speicherung fehlschlägt, funktioniert das SDK möglicherweise nicht korrekt
  • Das isId-Flag bestimmt die Persistenz: IDs bleiben über Sitzungen hinweg bestehen, Objekte sind sitzungsbezogen

Grenzfälle bei der Nutzeridentifikation:

  • Nach der Identifikation kann nicht zu anonymen Nutzer:innen zurückgekehrt werden
  • Ein Nutzerwechsel beendet die aktuelle Sitzung und startet eine neue
  • Der Verlauf anonymer Nutzer:innen bleibt bei der erstmaligen Identifikation erhalten
  • Der Verlauf wird zusammengeführt, wenn die:der Nutzer:in auf einem anderen Gerät existiert

Sitzungsmanagement:

  • Sitzungen laufen nach 30 Minuten Inaktivität ab (konfigurierbar)
  • openSession() gibt true für eine neue Sitzung zurück, false bei Wiederaufnahme
  • openSession() muss nach changeUser() oder setIdentifierToken() aufgerufen werden

Abo-Management:

  • Abo-Callbacks werden synchron ausgelöst, wenn Ereignisse auftreten
  • Entfernen Sie Abos, um Speicherlecks zu vermeiden
  • removeAllSubscriptions() löscht alle Abos auf einmal

Data-Flushing:

  • Automatischer Flush alle 10 Sekunden (konfigurierbar, Minimum: 3 Sekunden)
  • Flush kann stillschweigend fehlschlagen – verwenden Sie den Callback von requestImmediateDataFlush()
  • Daten werden in die Warteschlange gestellt, wenn das Netzwerk nicht verfügbar ist, und übertragen, wenn die Verbindung wiederhergestellt wird

Wichtige Hinweise zur Implementierung

  1. Die meisten Methoden sind asynchron: Asynchrone SDK-Methoden geben ein Promise zurück (verwenden Sie await oder .then()). Einige Konfigurations- und Hilfsmethoden (z. B. destroy, toggleLogging, setLogger) sind synchron; Details finden Sie in den TypeScript-Definitionen oder der Kurzreferenztabelle.

  2. Methoden können undefined zurückgeben: Wenn das SDK nicht initialisiert ist, geben die meisten Methoden undefined zurück, anstatt einen Fehler zu werfen. Prüfen Sie auf undefined, bevor Sie Rückgabewerte verwenden.

  3. Methoden können null zurückgeben: Einige Methoden geben null zurück, um „nicht gefunden“ anzuzeigen (z. B. gibt getUserId() null zurück, wenn die:der Nutzer:in anonym ist). Dies unterscheidet sich von undefined (SDK nicht initialisiert).

  4. Speicherschlüssel verwenden das isId-Flag: Der Parameter isId in den StorageManager-Methoden unterscheidet zwischen:
    • ID-Speicherung: Persistente Bezeichner (Geräte-ID, Nutzer-ID), die über Sitzungen hinweg bestehen bleiben sollten
    • Objekt-Speicherung: Sitzungsbezogene Daten, die gelöscht werden können
  5. SDK-Metadaten-Tags: Das Array sdkMetadata identifiziert die Plattform bzw. den Wrapper, der das SDK verwendet (z. B. ['npm'] oder [BrazeSdkMetadata.NPM]). Gültige Tags werden durch das Enum BrazeSdkMetadata definiert (wie npm, cdn, manu, shp, gg, kep), und das SDK fügt automatisch 'wjs' hinzu, um das JavaScript SDK zu kennzeichnen.

  6. Standard-NetworkManager: Wenn kein networkManager angegeben wird, verwendet das SDK eine Standardimplementierung, die globale fetch- und URL-APIs voraussetzt. Stellen Sie eine angepasste Implementierung bereit, falls diese nicht verfügbar sind.

  7. PushManager ist optional: Implementieren Sie PushManager nur, wenn Sie Push-Benachrichtigungsfunktionen benötigen. Andernfalls kann er weggelassen werden.

  8. Destroy und Bereinigung: Rufen Sie destroy() auf, wenn Sie das SDK herunterfahren müssen. Es kann immer nur eine aktive Sitzung gleichzeitig existieren; Sie müssen destroy() aufrufen, bevor Sie initialize() erneut aufrufen. Dadurch werden Timer gestoppt, Daten übertragen und Ressourcen freigegeben.

  9. Data-Flushing: Daten werden automatisch alle 10 Sekunden übertragen (konfigurierbar). Verwenden Sie requestImmediateDataFlush() für die sofortige Synchronisierung.

  10. Sitzungsmanagement: Rufen Sie openSession() immer nach changeUser() oder setIdentifierToken() auf, um das Erstellen doppelter anonymer Nutzer:innen zu vermeiden.

  11. Typsicherheit: Das SDK ist in TypeScript mit vollständigen Typdefinitionen geschrieben. Verwenden Sie TypeScript für die beste Entwicklungserfahrung und Typprüfung.

  12. Validierungsregeln: Ereignisnamen, Attributschlüssel und Eigenschaftsschlüssel unterliegen einer strengen Validierung (maximal 255 Zeichen, darf nicht mit $ beginnen, nur alphanumerische Zeichen und Satzzeichen). Ungültige Werte können ignoriert werden oder Fehler verursachen.

Debugging / Fehlerbehebung

Übergeben Sie die Option enableLogging: true an die Initialisierungsoptionen. Dies ist für die Entwicklung hilfreich, aber stellen Sie sicher, dass Sie diese Option entfernen oder einen alternativen Logger bereitstellen, bevor Sie Ihre Seite in die Produktionsumgebung überführen.

Kontakt

Bei Fragen wenden Sie sich bitte an [email protected].

Repository-Details und Beispielprojekte finden Sie unter https://github.com/braze-inc/braze-javascript-sdk.

New Stuff!