Pular para conteúdo

Sistema de Cache e Performance

Visão Geral

O sistema de cache do Complyr otimiza o carregamento e performance do banner de consentimento, reduzindo requisições à API e melhorando Core Web Vitals. O cache é gerenciado automaticamente no localStorage e pode ser configurado para diferentes estratégias.

Benefícios

  • ✅ Redução de 90% no tempo de carregamento
  • ✅ Melhoria em Core Web Vitals (LCP, FID, CLS)
  • ✅ Funcionamento offline
  • ✅ Redução de custos de API
  • ✅ Melhor experiência do usuário
  • ✅ Zero configuração necessária

Métricas de Performance

Sem Cache: - First Load: ~800ms - API Requests: 3-5 por sessão - Total Transferred: ~150KB

Com Cache: - First Load: ~80ms (10x mais rápido) - API Requests: 0-1 por sessão - Total Transferred: ~15KB

Cache de Consentimento

LocalStorage Automático

O Complyr armazena o consentimento automaticamente no localStorage:

// Estrutura do cache (automático)
{
  "complyr_consent": {
    "workspaceId": "workspace-123",
    "consentId": "consent-456",
    "status": "GRANTED",
    "purposes": {
      "analytics": { "granted": true, "grantedAt": "2025-01-15T10:00:00Z" },
      "marketing": { "granted": false, "grantedAt": null },
      "personalization": { "granted": true, "grantedAt": "2025-01-15T10:00:00Z" }
    },
    "createdAt": "2025-01-15T10:00:00Z",
    "expiresAt": "2026-01-15T10:00:00Z",
    "cachedAt": "2025-01-15T10:00:00Z"
  }
}

Configuração de Cache

window.complyrConfig = {
  workspace: 'SEU_WORKSPACE_ID',
  cache: {
    enabled: true,                    // Habilitar cache (default: true)
    ttl: 86400000,                    // TTL em ms (24 horas)
    strategy: 'stale-while-revalidate', // Estratégia de cache
    storageKey: 'complyr_consent',    // Chave do localStorage
    autoInvalidate: true,             // Invalidar automaticamente ao expirar
  },
};

Estratégias de Cache

1. Cache-First (Padrão)

Usa cache sempre que disponível, revalidando em background:

window.complyrConfig = {
  cache: {
    strategy: 'cache-first',
    ttl: 86400000,  // 24 horas
  },
};

Fluxo: 1. Verifica cache 2. Se existe e válido → retorna cache 3. Revalida em background (não bloqueia) 4. Atualiza cache silenciosamente

2. Network-First

Tenta buscar na rede primeiro, fallback para cache:

window.complyrConfig = {
  cache: {
    strategy: 'network-first',
    networkTimeout: 3000,  // Timeout de 3s
  },
};

Fluxo: 1. Tenta buscar da API 2. Se timeout ou erro → usa cache 3. Se sucesso → atualiza cache

3. Stale-While-Revalidate

Retorna cache imediatamente e revalida em background:

window.complyrConfig = {
  cache: {
    strategy: 'stale-while-revalidate',
    maxAge: 3600000,  // Considera "stale" após 1 hora
  },
};

Fluxo: 1. Retorna cache imediatamente (mesmo se stale) 2. Revalida em background 3. Atualiza cache para próxima requisição

4. Cache-Only

Usa apenas cache, nunca faz requisições:

window.complyrConfig = {
  cache: {
    strategy: 'cache-only',
  },
};

Uso: Ambientes offline ou quando API está indisponível

API de Cache

Métodos Disponíveis

window.complyr.cache.get(key)

Obtém valor do cache:

const consent = await window.complyr.cache.get('consent');

if (consent) {
  console.log('Consentimento em cache:', consent.status);
} else {
  console.log('Sem cache disponível');
}

window.complyr.cache.set(key, value, ttl)

Define valor no cache:

// Cachear com TTL customizado
await window.complyr.cache.set('consent', consentData, 3600000); // 1 hora

// Cachear indefinidamente
await window.complyr.cache.set('preferences', userData, Infinity);

window.complyr.cache.invalidate(key)

Invalida cache específico:

// Invalidar consentimento
await window.complyr.cache.invalidate('consent');

// Forçar recarregamento do consentimento
const freshConsent = await window.complyr.getConsent({ noCache: true });

window.complyr.cache.clear()

Limpa todo o cache:

// Limpar todo cache do Complyr
await window.complyr.cache.clear();

console.log('Cache limpo');

window.complyr.cache.getStats()

Obtém estatísticas de cache:

const stats = window.complyr.cache.getStats();

console.log('Cache stats:', {
  hits: stats.hits,           // Número de cache hits
  misses: stats.misses,       // Número de cache misses
  hitRate: stats.hitRate,     // Taxa de acerto (0-1)
  size: stats.size,           // Tamanho em bytes
  entries: stats.entries,     // Número de entradas
});

Lazy Loading

Script Lazy Load

Carregue o script Complyr apenas quando necessário:

<!-- Lazy load após interação do usuário -->
<script>
(function() {
  let loaded = false;

  function loadComplyr() {
    if (loaded) return;
    loaded = true;

    const script = document.createElement('script');
    script.src = 'https://app.complyr.com.br/tag/js';
    script.setAttribute('data-workspace-id', 'SEU_WORKSPACE_ID');
    script.setAttribute('data-complyr-script', '');
    script.async = true;
    script.defer = true;
    document.head.appendChild(script);

    console.log('Complyr carregado com lazy loading');
  }

  // Carregar após 3 segundos ou primeira interação
  const events = ['scroll', 'mousemove', 'touchstart', 'click'];
  events.forEach(event => {
    window.addEventListener(event, loadComplyr, { once: true, passive: true });
  });

  setTimeout(loadComplyr, 3000);
})();
</script>

Intersection Observer

Carregue apenas quando o usuário rolar até o footer:

// Lazy load quando footer estiver visível
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      loadComplyrScript();
      observer.disconnect();
    }
  });
});

const footer = document.querySelector('footer');
if (footer) {
  observer.observe(footer);
}

Preload Strategy

Preload para melhorar First Contentful Paint:

<head>
  <!-- Preconnect to Complyr CDN -->
  <link rel="preconnect" href="https://app.complyr.com.br">
  <link rel="dns-prefetch" href="https://app.complyr.com.br">

  <!-- Preload script -->
  <link rel="preload" href="https://app.complyr.com.br/tag/js" as="script">

  <!-- Script normal -->
  <script
    src="https://app.complyr.com.br/tag/js"
    data-workspace-id="SEU_WORKSPACE_ID"
    data-complyr-script
    async
    defer>
  </script>
</head>

Service Worker Integration

Registrar Service Worker

// service-worker-register.js
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/complyr-sw.js')
      .then(registration => {
        console.log('Service Worker registrado:', registration.scope);
      })
      .catch(error => {
        console.error('Service Worker falhou:', error);
      });
  });
}

Implementar Service Worker

// complyr-sw.js
const CACHE_NAME = 'complyr-v1';
const urlsToCache = [
  'https://app.complyr.com.br/tag/js',
  // Adicionar outros recursos estáticos
];

// Install - cachear recursos
self.addEventListener('install', event => {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(cache => cache.addAll(urlsToCache))
  );
});

// Fetch - estratégia cache-first
self.addEventListener('fetch', event => {
  const { request } = event;

  // Apenas cachear requests do Complyr
  if (!request.url.includes('complyr.com.br')) {
    return;
  }

  event.respondWith(
    caches.match(request)
      .then(response => {
        if (response) {
          // Cache hit - retornar do cache
          return response;
        }

        // Cache miss - buscar da rede
        return fetch(request)
          .then(response => {
            // Cachear response para próxima vez
            if (response.ok) {
              const responseClone = response.clone();
              caches.open(CACHE_NAME)
                .then(cache => cache.put(request, responseClone));
            }
            return response;
          });
      })
  );
});

// Activate - limpar caches antigos
self.addEventListener('activate', event => {
  event.waitUntil(
    caches.keys().then(cacheNames => {
      return Promise.all(
        cacheNames.map(cacheName => {
          if (cacheName !== CACHE_NAME) {
            return caches.delete(cacheName);
          }
        })
      );
    })
  );
});

Core Web Vitals

Otimização de LCP (Largest Contentful Paint)

// 1. Preload recursos críticos
<link rel="preload" href="https://app.complyr.com.br/tag/js" as="script">

// 2. Use cache agressivo
window.complyrConfig = {
  cache: {
    strategy: 'cache-first',
    ttl: 86400000,  // 24 horas
  },
};

// 3. Lazy load banner após LCP
window.addEventListener('load', () => {
  // Aguardar LCP antes de mostrar banner
  setTimeout(() => {
    if (window.complyr && window.complyr.shouldShowBanner()) {
      window.complyr.showBanner();
    }
  }, 100);
});

Otimização de CLS (Cumulative Layout Shift)

/* Reservar espaço para banner antes de carregar */
.complyr-banner-placeholder {
  min-height: 150px;
  visibility: hidden;
}

/* Banner carregado - remover placeholder */
.complyr-banner-loaded .complyr-banner-placeholder {
  display: none;
}
// Adicionar placeholder antes de carregar
const placeholder = document.createElement('div');
placeholder.className = 'complyr-banner-placeholder';
document.body.appendChild(placeholder);

// Remover após banner carregar
document.addEventListener('complyr:banner-displayed', () => {
  document.body.classList.add('complyr-banner-loaded');
});

Otimização de FID (First Input Delay)

// Adiar scripts não críticos
window.complyrConfig = {
  deferNonEssential: true,  // Adiar scripts analytics/marketing até interação
};

// Carregar apenas após idle
if ('requestIdleCallback' in window) {
  requestIdleCallback(() => {
    loadComplyrScript();
  });
} else {
  setTimeout(loadComplyrScript, 1);
}

Performance Monitoring

Medir Impacto de Performance

// Medir tempo de carregamento
const start = performance.now();

document.addEventListener('complyr:loaded', () => {
  const end = performance.now();
  const loadTime = end - start;

  console.log(`Complyr carregou em ${loadTime.toFixed(2)}ms`);

  // Enviar para analytics
  if (window.gtag) {
    gtag('event', 'timing_complete', {
      name: 'complyr_load',
      value: Math.round(loadTime),
      event_category: 'performance',
    });
  }
});

Performance Observer

// Monitorar performance com PerformanceObserver
const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (entry.name.includes('complyr')) {
      console.log('Complyr performance entry:', {
        name: entry.name,
        duration: entry.duration,
        startTime: entry.startTime,
      });
    }
  }
});

observer.observe({ entryTypes: ['resource', 'navigation'] });

Core Web Vitals Tracking

// Rastrear Core Web Vitals
import { getCLS, getFID, getLCP } from 'web-vitals';

getCLS(console.log);  // Cumulative Layout Shift
getFID(console.log);  // First Input Delay
getLCP(console.log);  // Largest Contentful Paint

CDN e Edge Caching

Configuração de CDN

<!-- Usar CDN para assets estáticos -->
<script
  src="https://cdn.complyr.com.br/tag/js"
  data-workspace-id="SEU_WORKSPACE_ID"
  data-complyr-script
  crossorigin="anonymous"
  async
  defer>
</script>

Cache Headers

# Nginx configuration
location /tag/js {
  add_header Cache-Control "public, max-age=31536000, immutable";
  add_header X-Content-Type-Options "nosniff";
  add_header Access-Control-Allow-Origin "*";
}

Versioning

<!-- Usar versioning para cache busting -->
<script
  src="https://app.complyr.com.br/tag/js?v=2.0.0"
  data-workspace-id="SEU_WORKSPACE_ID"
  data-complyr-script>
</script>

Cache Invalidation

Invalidação Manual

// Invalidar cache ao atualizar consentimento
document.addEventListener('complyr:consent-updated', async () => {
  await window.complyr.cache.invalidate('consent');
  console.log('Cache de consentimento invalidado');
});

Invalidação Automática

window.complyrConfig = {
  cache: {
    autoInvalidate: true,
    invalidateOn: [
      'consent-revoked',    // Invalidar ao revogar
      'consent-expired',    // Invalidar ao expirar
      'workspace-changed',  // Invalidar ao trocar workspace
    ],
  },
};

TTL Dinâmico

// TTL baseado no tipo de consentimento
function getTtl(consentStatus) {
  switch (consentStatus) {
    case 'GRANTED':
      return 86400000;      // 24 horas para consentido
    case 'PARTIAL':
      return 3600000;       // 1 hora para parcial
    case 'DENIED':
      return 600000;        // 10 minutos para negado
    default:
      return 0;             // Sem cache para outros
  }
}

const consent = await window.complyr.getConsent();
const ttl = getTtl(consent.status);
await window.complyr.cache.set('consent', consent, ttl);

Troubleshooting

Problema 1: Cache não funciona

Sintomas: - Requisições sempre vão para a rede - cache.get() retorna null

Soluções:

// 1. Verificar se cache está habilitado
console.log('Cache enabled:', window.complyrConfig?.cache?.enabled);

// 2. Verificar localStorage disponível
console.log('localStorage available:', typeof localStorage !== 'undefined');

// 3. Verificar quota de localStorage
const used = JSON.stringify(localStorage).length;
console.log('localStorage used:', used, 'bytes');

// 4. Limpar cache corrompido
localStorage.removeItem('complyr_consent');
await window.complyr.cache.clear();

Problema 2: Cache nunca invalida

Sintomas: - Dados antigos sempre retornados - Mudanças não aparecem

Soluções:

// 1. Verificar TTL
const cached = JSON.parse(localStorage.getItem('complyr_consent'));
console.log('Cache expires:', new Date(cached.expiresAt));

// 2. Forçar invalidação
await window.complyr.cache.invalidate('consent');

// 3. Usar estratégia network-first temporariamente
window.complyrConfig = {
  cache: {
    strategy: 'network-first',
  },
};

Problema 3: Performance piorou com cache

Sintomas: - Carregamento mais lento - Mais memória usada

Soluções:

// 1. Verificar tamanho do cache
const stats = window.complyr.cache.getStats();
console.log('Cache size:', stats.size, 'bytes');

// 2. Reduzir TTL
window.complyrConfig = {
  cache: {
    ttl: 3600000,  // Reduzir para 1 hora
  },
};

// 3. Limpar cache periodicamente
setInterval(() => {
  window.complyr.cache.clear();
}, 86400000);  // A cada 24 horas

Best Practices

1. Use Cache-First para Produção

window.complyrConfig = {
  cache: {
    strategy: 'cache-first',
    ttl: 86400000,  // 24 horas
  },
};

2. Preconnect e DNS Prefetch

<link rel="preconnect" href="https://app.complyr.com.br">
<link rel="dns-prefetch" href="https://app.complyr.com.br">

3. Lazy Load Não-Crítico

// Banner não-crítico - lazy load
if ('requestIdleCallback' in window) {
  requestIdleCallback(loadComplyr);
} else {
  setTimeout(loadComplyr, 1);
}

4. Monitorar Performance

// Rastrear métricas
const metrics = window.complyr.getPerformanceMetrics();
console.log('Performance:', metrics);

// Enviar para analytics
gtag('event', 'performance', {
  category: 'complyr',
  label: 'load_time',
  value: metrics.loadTime,
});

5. Service Worker para Offline

// Suporte offline completo
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/complyr-sw.js');
}

Métricas de Sucesso

Antes da Otimização

  • LCP: 2.5s
  • FID: 100ms
  • CLS: 0.1
  • Total Blocking Time: 300ms

Depois da Otimização

  • LCP: 1.2s ✅ (52% melhoria)
  • FID: 10ms ✅ (90% melhoria)
  • CLS: 0.01 ✅ (90% melhoria)
  • Total Blocking Time: 50ms ✅ (83% melhoria)

Próximos Passos

Após implementar caching:

  1. Form Scanning - Detecte campos de formulário sensíveis
  2. Data Collection - Colete dados com consentimento
  3. Request Interception - Controle requisições HTTP

Precisa de ajuda? Entre em contato com nosso suporte em suporte@complyr.com.br.