Web Development

Web Performance Optimization: Panduan Lengkap

Pelajari cara mengoptimalkan performa website dari Core Web Vitals, lazy loading, code splitting, image optimization, caching, hingga CDN dengan contoh kode praktis

1. Pengenalan Web Performance

Web Performance Optimization adalah serangkaian teknik dan strategi untuk membuat website memuat lebih cepat, merespons lebih responsif, dan memberikan pengalaman pengguna yang lebih baik. Performa website bukan sekadar soal kecepatan β€” ini langsung memengaruhi SEO ranking, conversion rate, dan user retention.

Menurut riset Google, 53% pengguna mobile meninggalkan halaman yang memuat lebih dari 3 detik. Setiap peningkatan 0.1 detik pada waktu muat dapat meningkatkan conversion rate hingga 8%. Ini menjadikan performa web sebagai faktor kritis dalam kesuksesan bisnis online.

Mengapa Performa Penting?

Faktor Dampak Data
SEO RankingGoogle menggunakan Core Web Vitals sebagai ranking signalHalaman lambat turun peringkat
User ExperiencePengguna mengharapkan halaman dimuat dalam < 3 detik53% bounce jika > 3s
Conversion RateSetiap 100ms penundaan menurunkan penjualan 1%Amazon kehilangan $1.6B per detik
Bandwidth CostOptimasi mengurangi penggunaan data dan biaya serverHingga 60% penghematan
AccessibilityWebsite cepat lebih mudah diakses di jaringan lambatPengguna 3G/4G terbantu

Faktor yang Mempengaruhi Performa

Diagram: Lifecycle Pemuatan Halaman Web
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                  LIFECYCLE PEMUATAN HALAMAN                  β”‚
β”‚                                                              β”‚
β”‚  Browser        Network          Server        Rendering     β”‚
β”‚  Request        Transfer         Process       Display       β”‚
β”‚                                                              β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚ DNS β”‚ ───► β”‚  TCP +  β”‚ ──► β”‚ Server  │──►│  Parse   β”‚  β”‚
β”‚  β”‚Lookupβ”‚      β”‚   TLS   β”‚     β”‚ Responseβ”‚   β”‚  HTML    β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                                                    β”‚        β”‚
β”‚                                              β”Œβ”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”  β”‚
β”‚                                              β”‚  Build    β”‚  β”‚
β”‚                                              β”‚   DOM     β”‚  β”‚
β”‚                                              β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                                                    β”‚        β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜        β”‚
β”‚  β”‚                                                          β”‚
β”‚  β–Ό                                                          β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚  Fetch  │──►│  CSSOM  │──►│ Render  │──►│  Paint   β”‚  β”‚
β”‚  β”‚  CSS    β”‚   β”‚  Build  β”‚   β”‚  Tree   β”‚   β”‚  & Compositeβ”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
πŸ’‘ Tips

Performa web adalah proses berkelanjutan. Mulailah dengan mengukur performa saat ini menggunakan tools seperti Lighthouse, PageSpeed Insights, atau WebPageTest, lalu perbaiki secara bertahap berdasarkan metrik yang paling bermasalah.

2. Core Web Vitals

Core Web Vitals adalah tiga metrik inti yang diukur Google untuk menilai pengalaman pengguna pada halaman web. Metrik-metrik ini menjadi faktor ranking resmi sejak tahun 2021 dan terus diperbarui.

Tiga Metrik Utama

Metrik Kepanjangan Yang Diukur Target
LCPLargest Contentful PaintWaktu render elemen terbesar≀ 2.5 detik
INPInteraction to Next PaintResponsivitas interaksi pengguna≀ 200 ms
CLSCumulative Layout ShiftPerpindahan layout visual≀ 0.1

LCP (Largest Contentful Paint)

LCP mengukur waktu yang dibutuhkan untuk merender elemen konten terbesar yang terlihat di viewport. Elemen yang biasanya menjadi LCP meliputi gambar hero, video, atau blok teks besar.

JavaScript β€” Mengukur LCP
// Mengukur LCP menggunakan PerformanceObserver
const lcpObserver = new PerformanceObserver((entryList) => {
  const entries = entryList.getEntries();
  const lastEntry = entries[entries.length - 1];

  console.log('LCP:', lastEntry.startTime.toFixed(2), 'ms');
  console.log('Element:', lastEntry.element);

  // Kategorikan skor
  if (lastEntry.startTime <= 2500) {
    console.log('βœ… LCP Good');
  } else if (lastEntry.startTime <= 4000) {
    console.log('⚠️ LCP Needs Improvement');
  } else {
    console.log('❌ LCP Poor');
  }
});

lcpObserver.observe({ type: 'largest-contentful-paint', buffered: true });

Teknik Optimasi LCP

HTML β€” Preload Critical Resources
<!-- Preload font yang digunakan di hero section -->
<link rel="preload" href="/fonts/inter-bold.woff2"
      as="font" type="font/woff2" crossorigin>

<!-- Preload gambar hero (LCP element) -->
<link rel="preload" href="/images/hero.webp"
      as="image" type="image/webp">

<!-- Preconnect ke domain CDN -->
<link rel="preconnect" href="https://cdn.example.com" crossorigin>

<!-- DNS Prefetch untuk domain pihak ketiga -->
<link rel="dns-prefetch" href="https://analytics.google.com">

<!-- Render-blocking CSS β€” inline critical CSS -->
<style>
  /* Critical CSS di-inline langsung di HTML */
  .hero { display: flex; align-items: center; min-height: 100vh; }
  .hero h1 { font-size: 3rem; font-weight: 700; color: #fff; }
</style>

<!-- Non-critical CSS dimuat secara async -->
<link rel="preload" href="/css/full.css" as="style"
      onload="this.onload=null;this.rel='stylesheet'">

INP (Interaction to Next Paint)

INP mengukur responsivitas halaman terhadap interaksi pengguna β€” klik, ketuk, dan penekanan tombol. INP menggantikan FID (First Input Delay) sebagai metrik responsivitas utama.

JavaScript β€” Optimasi INP
// ❌ Buruk: Blocking main thread dengan operasi berat
button.addEventListener('click', () => {
  // Operasi sinkron yang berat β€” memblokir respons
  for (let i = 0; i < 1000000; i++) {
    heavyCalculation(i);
  }
  updateUI();
});

// βœ… Baik: Gunakan requestIdleCallback atau setTimeout
button.addEventListener('click', () => {
  // Bagi pekerjaan berat menjadi chunk kecil
  const items = Array.from({ length: 1000000 });

  function processChunk(startIndex) {
    const chunk = items.slice(startIndex, startIndex + 1000);

    chunk.forEach(item => heavyCalculation(item));

    if (startIndex + 1000 < items.length) {
      // Yield kembali ke main thread
      requestIdleCallback(() => processChunk(startIndex + 1000));
    } else {
      updateUI();
    }
  }

  requestIdleCallback(() => processChunk(0));
});

// βœ… Baik: Gunakan Web Workers untuk komputasi berat
const worker = new Worker('heavy-task.js');
worker.postMessage({ data: largeDataset });
worker.onmessage = (e) => {
  updateUI(e.data.result);
};

CLS (Cumulative Layout Shift)

CLS mengukur perpindahan elemen visual yang tidak terduga selama pemuatan halaman. Layout shift terjadi ketika elemen berubah posisi setelah render awal.

CSS β€” Mencegah Layout Shift
/* Selalu tentukan dimensi gambar */
img, video {
  max-width: 100%;
  height: auto;
  aspect-ratio: attr(width) / attr(height);
}

/* Atau gunakan aspect-ratio property */
.hero-image {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
}

/* Placeholder untuk iklan / konten dinamis */
.ad-container {
  min-height: 250px;  /* Reserve space */
  background: #1a1a1a;
}

/* Font loading β€” hindari FOIT */
@font-face {
  font-family: 'CustomFont';
  src: url('/fonts/custom.woff2') format('woff2');
  font-display: swap; /* Tampilkan fallback font dulu */
}

/* Hindari inject konten di atas konten existing */
.notification-banner {
  /* Gunakan position: sticky bukan absolute */
  position: sticky;
  top: 0;
  z-index: 1000;
}
⚠️ Peringatan

Jangan sembunyikan layout shift dengan trik visual. Google tetap mendeteksi pergeseran elemen meskipun tidak terlihat oleh mata. Pastikan setiap elemen yang dimuat secara dinamis memiliki ruang yang dialokasikan sebelum konten muncul.

3. Lazy Loading

Lazy loading adalah teknik menunda pemuatan resource yang tidak langsung dibutuhkan hingga pengguna membutuhkannya. Ini sangat efektif untuk gambar, video, dan komponen JavaScript yang berada di bawah fold (tidak terlihat di viewport awal).

Native Lazy Loading (HTML)

HTML β€” Native Lazy Loading
<!-- Gambar di bawah fold: gunakan loading="lazy" -->
<img src="/images/product-1.webp"
     alt="Produk 1"
     width="400" height="300"
     loading="lazy"
     decoding="async">

<!-- Gambar di atas fold: JANGAN lazy load -->
<img src="/images/hero.webp"
     alt="Hero Banner"
     width="1200" height="600"
     loading="eager"
     fetchpriority="high">

<!-- Lazy load iframe (video YouTube, Google Maps) -->
<iframe src="https://www.youtube.com/embed/VIDEO_ID"
        loading="lazy"
        width="560" height="315"
        title="Video Tutorial"
        allowfullscreen></iframe>

Intersection Observer API

Untuk kontrol lebih lanjut, gunakan Intersection Observer untuk lazy load elemen kapan pun sesuai kebutuhan:

JavaScript β€” Intersection Observer
// Lazy load gambar dengan Intersection Observer
function setupLazyImages() {
  const images = document.querySelectorAll('img[data-src]');

  const imageObserver = new IntersectionObserver((entries, observer) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        const img = entry.target;

        // Ganti data-src dengan src
        img.src = img.dataset.src;

        // Jika ada srcset
        if (img.dataset.srcset) {
          img.srcset = img.dataset.srcset;
        }

        img.classList.add('loaded');
        observer.unobserve(img);
      }
    });
  }, {
    rootMargin: '200px 0px',  // Mulai load 200px sebelum terlihat
    threshold: 0.01
  });

  images.forEach(img => imageObserver.observe(img));
}

// Jalankan saat DOM siap
document.addEventListener('DOMContentLoaded', setupLazyImages);

// Contoh HTML:
// <img data-src="/images/foto.webp"
//      src="/images/placeholder.svg"
//      alt="Foto" width="400" height="300">

Lazy Load Komponen JavaScript

React β€” Lazy Load Komponen
import { lazy, Suspense } from 'react';

// Lazy load komponen berat
const ChartDashboard = lazy(() => import('./ChartDashboard'));
const VideoPlayer = lazy(() => import('./VideoPlayer'));
const MapComponent = lazy(() => import('./MapComponent'));

function Dashboard() {
  return (
    <div className="dashboard">
      <h1>Dashboard</h1>

      {/* Komponen dimuat hanya saat dibutuhkan */}
      <Suspense fallback={<div className="skeleton">Memuat chart...</div>}>
        <ChartDashboard />
      </Suspense>

      <Suspense fallback={<div className="skeleton">Memuat video...</div>}>
        <VideoPlayer src="/videos/tutorial.mp4" />
      </Suspense>

      <Suspense fallback={<div className="skeleton">Memuat peta...</div>}>
        <MapComponent lat={-6.2} lng={106.8} />
      </Suspense>
    </div>
  );
}

4. Code Splitting

Code splitting adalah teknik memecah bundle JavaScript besar menjadi beberapa chunk yang lebih kecil. Alih-alih memuat seluruh aplikasi sekaligus, pengguna hanya memuat kode yang dibutuhkan untuk halaman saat ini.

Mengapa Code Splitting Penting?

Tanpa Code Splitting Dengan Code Splitting
Satu bundle.js berukuran 2 MBMultiple chunks 50-200 KB each
Memuat semua kode sekaligusHanya memuat kode halaman aktif
TTI lambat (5-10 detik)TTI cepat (1-3 detik)
Cache tidak efisienPerubahan kecil = re-download kecil

Dynamic Import

JavaScript β€” Dynamic Import
// ❌ Static import β€” semua dimuat sekaligus
import { Chart } from 'chart.js';
import moment from 'moment';
import lodash from 'lodash';

// βœ… Dynamic import β€” dimuat saat dibutuhkan
async function renderChart(data) {
  // Chart.js dimuat hanya saat fungsi dipanggil
  const { Chart } = await import('chart.js/auto');
  const chart = new Chart(document.getElementById('chart'), {
    type: 'bar',
    data: data
  });
  return chart;
}

// Dynamic import dengan error handling
async function loadAnalytics() {
  try {
    const module = await import('./analytics.js');
    module.trackPageView();
  } catch (error) {
    console.error('Gagal memuat modul analytics:', error);
    // Fallback: kirim data dasar tanpa library
    sendBasicAnalytics();
  }
}

// Conditional loading β€” hanya di halaman tertentu
if (window.location.pathname === '/dashboard') {
  import('./dashboard-charts.js').then(module => {
    module.initCharts();
  });
}

Code Splitting dengan Vite / Webpack

vite.config.js β€” Manual Chunk Splitting
// vite.config.js
import { defineConfig } from 'vite';

export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        // Pisahkan vendor libraries ke chunk terpisah
        manualChunks: {
          'vendor-react': ['react', 'react-dom'],
          'vendor-utils': ['lodash', 'date-fns'],
          'vendor-charts': ['chart.js', 'd3'],
        },
      },
    },
    // Batasi ukuran chunk warning
    chunkSizeWarningLimit: 500, // 500 KB
  },
});

Route-based Code Splitting

React β€” Route-based Splitting
import { lazy, Suspense } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';

// Setiap halaman di-split menjadi chunk terpisah
const HomePage = lazy(() => import('./pages/HomePage'));
const ProductPage = lazy(() => import('./pages/ProductPage'));
const CheckoutPage = lazy(() => import('./pages/CheckoutPage'));
const ProfilePage = lazy(() => import('./pages/ProfilePage'));
const AdminPage = lazy(() => import('./pages/AdminPage'));

function App() {
  return (
    <BrowserRouter>
      <Suspense fallback={<div className="page-loader">Loading...</div>}>
        <Routes>
          <Route path="/" element={<HomePage />} />
          <Route path="/produk/:id" element={<ProductPage />} />
          <Route path="/checkout" element={<CheckoutPage />} />
          <Route path="/profil" element={<ProfilePage />} />
          <Route path="/admin/*" element={<AdminPage />} />
        </Routes>
      </Suspense>
    </BrowserRouter>
  );
}

5. Image Optimization

Gambar biasanya menyumbang 50-70% total ukuran halaman web. Mengoptimalkan gambar adalah salah satu cara paling efektif untuk meningkatkan performa.

Format Gambar Modern

Format Kompresi Dukungan Browser Cocok Untuk
WebP25-34% lebih kecil dari JPEG97%+ browserGambar umum, foto
AVIF50% lebih kecil dari JPEG~90% browser modernFoto berkualitas tinggi
SVGVektor, ukuran sangat kecilSemua browserIcon, logo, ilustrasi
JPEGLossy, kompresi baikSemua browserFallback untuk browser lama
PNGLossless, transparansiSemua browserGambar dengan transparansi

Responsive Images dengan picture Tag

HTML β€” Responsive Images
<!-- Gunakan <picture> untuk format modern dengan fallback -->
<picture>
  <!-- AVIF untuk browser yang mendukung (terkecil) -->
  <source
    srcset="/images/hero-400.avif 400w,
            /images/hero-800.avif 800w,
            /images/hero-1200.avif 1200w"
    sizes="(max-width: 600px) 400px,
           (max-width: 1024px) 800px,
           1200px"
    type="image/avif">

  <!-- WebP sebagai opsi kedua -->
  <source
    srcset="/images/hero-400.webp 400w,
            /images/hero-800.webp 800w,
            /images/hero-1200.webp 1200w"
    sizes="(max-width: 600px) 400px,
           (max-width: 1024px) 800px,
           1200px"
    type="image/webp">

  <!-- JPEG sebagai fallback -->
  <img
    src="/images/hero-800.jpg"
    alt="Hero Banner"
    width="1200" height="600"
    loading="eager"
    fetchpriority="high">
</picture>

<!-- Menggunakan srcset untuk resolusi berbeda -->
<img
  srcset="/images/foto-small.webp 480w,
          /images/foto-medium.webp 768w,
          /images/foto-large.webp 1200w"
  sizes="(max-width: 600px) 100vw,
         (max-width: 1024px) 50vw,
         33vw"
  src="/images/foto-medium.webp"
  alt="Foto produk"
  loading="lazy"
  decoding="async"
  width="800" height="600">

Optimasi Gambar dengan Script

Bash β€” Optimasi dengan Sharp/Node.js
# Instal Sharp β€” library pemrosesan gambar untuk Node.js
npm install sharp

# Atau gunakan CLI tools
npm install -g @squoosh/cli    # Google Squoosh CLI
npm install -g imagemin-cli     # Imagemin CLI

# Contoh: Konversi batch ke WebP menggunakan sharp
node -e "
const sharp = require('sharp');
const fs = require('fs');
const path = require('path');

const inputDir = './images/original';
const outputDir = './images/optimized';

fs.readdirSync(inputDir).forEach(file => {
  const input = path.join(inputDir, file);
  const output = path.join(outputDir,
    file.replace(/\.(jpg|png)$/, '.webp'));

  sharp(input)
    .resize({ width: 1200, withoutEnlargement: true })
    .webp({ quality: 80 })
    .toFile(output)
    .then(info => console.log(file, 'β†’', info.size / 1024, 'KB'));
});
"
πŸ’‘ Tips CDN Image Optimization

Layanan CDN modern seperti Cloudflare Images, Cloudinary, dan imgix bisa melakukan transformasi gambar secara otomatis β€” resize, crop, konversi format, dan kompresi β€” hanya dengan mengubah URL gambar. Ini menghilangkan kebutuhan untuk membuat versi gambar secara manual.

6. Caching Strategies

Caching menyimpan salinan resource di lokasi yang lebih dekat dengan pengguna sehingga halaman dimuat lebih cepat saat dikunjungi kembali. Strategi caching yang tepat dapat mengurangi waktu muat hingga 80%.

Browser Caching (HTTP Headers)

Nginx β€” Cache Headers
# /etc/nginx/conf.d/cache.conf

# Cache aset statis (CSS, JS, gambar, font) selama 1 tahun
location ~* \.(css|js|jpg|jpeg|png|gif|webp|avif|svg|woff2|woff)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
    add_header Vary "Accept-Encoding";
}

# Cache HTML selama 5 menit dengan revalidation
location ~* \.html$ {
    expires 5m;
    add_header Cache-Control "public, must-revalidate";
}

# API response β€” cache 1 jam
location /api/ {
    expires 1h;
    add_header Cache-Control "public, stale-while-revalidate=3600";
}

# Tidak cache halaman sensitif
location /admin/ {
    add_header Cache-Control "no-store, no-cache, must-revalidate";
    add_header Pragma "no-cache";
}

Service Worker Caching

JavaScript β€” Service Worker Cache API
// service-worker.js β€” Cache-First Strategy untuk aset statis
const CACHE_NAME = 'beebane-v1';
const STATIC_ASSETS = [
  '/',
  '/index.html',
  '/css/style.css',
  '/js/app.js',
  '/images/logo_beebane.png',
  '/offline.html',
];

// Install: cache aset statis
self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME).then(cache => {
      return cache.addAll(STATIC_ASSETS);
    })
  );
  self.skipWaiting();
});

// Activate: hapus cache lama
self.addEventListener('activate', (event) => {
  event.waitUntil(
    caches.keys().then(keys => {
      return Promise.all(
        keys.filter(key => key !== CACHE_NAME)
            .map(key => caches.delete(key))
      );
    })
  );
  self.clients.claim();
});

// Fetch: strategi cache-first untuk aset, network-first untuk API
self.addEventListener('fetch', (event) => {
  const url = new URL(event.request.url);

  // Aset statis: cache-first
  if (STATIC_ASSETS.includes(url.pathname)) {
    event.respondWith(
      caches.match(event.request).then(cached => {
        return cached || fetch(event.request);
      })
    );
    return;
  }

  // API: network-first dengan fallback ke cache
  if (url.pathname.startsWith('/api/')) {
    event.respondWith(
      fetch(event.request)
        .then(response => {
          const clone = response.clone();
          caches.open(CACHE_NAME).then(cache => {
            cache.put(event.request, clone);
          });
          return response;
        })
        .catch(() => caches.match(event.request))
    );
    return;
  }
});

Strategi Caching yang Umum

Strategi Cara Kerja Cocok Untuk
Cache-FirstAmbil dari cache dulu, baru networkAset statis (CSS, JS, gambar)
Network-FirstAmbil dari network dulu, cache sebagai fallbackHalaman HTML, data dinamis
Stale-While-RevalidateTampilkan cache sambil update di backgroundAPI yang sering berubah
Network-OnlySelalu ambil dari networkData real-time, login

7. Content Delivery Network (CDN)

CDN (Content Delivery Network) adalah jaringan server yang tersebar di seluruh dunia yang menyimpan salinan konten statis website Anda. Saat pengguna mengakses situs, konten dikirimkan dari server terdekat secara geografis β€” mengurangi latency secara signifikan.

Cara Kerja CDN

Diagram: Cara Kerja CDN
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    CARA KERJA CDN                             β”‚
β”‚                                                               β”‚
β”‚  Pengguna Jakarta ───► PoP Singapore ───► Origin Server US   β”‚
β”‚       β”‚                    β”‚                     β”‚            β”‚
β”‚       β”‚     [Cache HIT]    β”‚     [Cache MISS]    β”‚            β”‚
β”‚       β”‚     Response: 20ms β”‚     Fetch dari originβ”‚            β”‚
β”‚       β—„β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     Simpan di cache  β”‚            β”‚
β”‚                                              β—„β”€β”€β”€β”€β”˜            β”‚
β”‚                                                               β”‚
β”‚  Pengguna Tokyo ───► PoP Tokyo ───► [Cache ada]              β”‚
β”‚       β”‚                   β”‚                                   β”‚
β”‚       β”‚    Response: 15ms  β”‚                                   β”‚
β”‚       β—„β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                   β”‚
β”‚                                                               β”‚
β”‚  Server CDN tersebar di 200+ lokasi worldwide                β”‚
β”‚  Latency turun dari 300ms β†’ 20ms = 15x lebih cepat          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Populer CDN Services

CDN Free Plan PoP Locations Fitur Utama
Cloudflareβœ… Ya300+ kotaDDoS protection, Workers, image optimization
Netlifyβœ… YaGlobal edgeDeploy previews, serverless functions
Vercelβœ… YaEdge networkNext.js optimization, ISR, Edge Functions
AWS CloudFrontFree tier400+ PoPIntegrasi AWS, Lambda@Edge
Bunny CDNPay-as-go114 PoPHarga murah, video streaming

Konfigurasi CDN Dasar

HTML β€” Menggunakan CDN untuk Libraries
<!-- Font dari Google Fonts CDN dengan preconnect -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap" rel="stylesheet">

<!-- Self-host lebih baik daripada CDN pihak ketiga -->
<!-- Download font dan simpan di server/CDN sendiri -->
<style>
@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter-variable.woff2') format('woff2-variations');
  font-weight: 100 900;
  font-display: swap;
}
</style>

<!-- Subresource Integrity β€” keamanan CDN pihak ketiga -->
<script src="https://cdn.jsdelivr.net/npm/react@18/umd/react.production.min.js"
        integrity="sha384-xxx..."
        crossorigin="anonymous"></script>
⚠️ Keamanan CDN

Saat menggunakan CDN pihak ketiga, selalu gunakan atribut integrity (SRI β€” Subresource Integrity) untuk memverifikasi bahwa file yang dikirim tidak dimodifikasi oleh pihak lain. Selain itu, self-hosting library di CDN sendiri lebih aman dan tidak bergantung pada pihak ketiga.

8. Tools Pengukuran Performa

Sebelum mengoptimasi, Anda perlu mengukur terlebih dahulu. Berikut tools yang direkomendasikan untuk mengukur dan menganalisis performa website:

Tools Tipe Yang Diukur
Google LighthouseLabPerformance score, accessibility, SEO, best practices
PageSpeed InsightsLab + FieldCore Web Vitals, LCP, INP, CLS
Chrome DevToolsLabNetwork waterfall, performance profiling, coverage
WebPageTestLabDetailed waterfall, filmstrip, multi-location test
CrUX DashboardFieldReal user metrics dari Chrome users
SpeedCurveLab + FieldPerformance monitoring, visual regression

Menggunakan Lighthouse dari CLI

Bash β€” Lighthouse CLI
# Instal Lighthouse globally
npm install -g lighthouse

# Jalankan audit pada halaman website
lighthouse https://example.com \
  --output=html \
  --output-path=./report.html \
  --chrome-flags="--headless --no-sandbox" \
  --only-categories=performance

# Jalankan untuk mobile
lighthouse https://example.com \
  --preset=perf \
  --form-factor=mobile \
  --throttling-method=simulate

# Bandingkan performa sebelum dan sesudah optimasi
lighthouse https://example.com --output=json --output-path=before.json
# ... lakukan optimasi ...
lighthouse https://example.com --output=json --output-path=after.json

# Hasil umum yang ingin dicapai:
# Performance Score: β‰₯ 90
# LCP: ≀ 2.5s
# INP: ≀ 200ms
# CLS: ≀ 0.1

9. Best Practices Ringkas

Berikut checklist cepat untuk optimasi performa web yang harus Anda terapkan:

βœ… Checklist Web Performance
  • Minifikasi CSS, JavaScript, dan HTML untuk mengurangi ukuran file
  • Kompresi menggunakan Brotli (lebih baik dari Gzip) untuk transfer data
  • Tree shaking untuk menghapus kode yang tidak digunakan dari bundle
  • Font optimization β€” gunakan font-display: swap dan subset karakter
  • Critical CSS β€” inline CSS penting, defer sisanya
  • Reduce third-party scripts β€” audit dan hapus script yang tidak perlu
  • HTTP/2 atau HTTP/3 β€” gunakan multiplexing untuk parallel loading
  • Resource hints β€” preload, prefetch, preconnect untuk resource kritis
  • Server-side rendering (SSR) atau Static Site Generation (SSG) untuk FCP cepat
  • Monitor terus-menerus β€” gunakan RUM (Real User Monitoring) untuk data field
Nginx β€” Konfigurasi Kompresi Brotli
# /etc/nginx/conf.d/compression.conf

# Brotli compression (lebih efisien dari Gzip)
brotli on;
brotli_comp_level 6;
brotli_types
  text/plain
  text/css
  text/javascript
  application/javascript
  application/json
  application/xml
  image/svg+xml;

# Gzip sebagai fallback
gzip on;
gzip_comp_level 6;
gzip_min_length 256;
gzip_vary on;
gzip_types
  text/plain
  text/css
  text/javascript
  application/javascript
  application/json
  image/svg+xml;

10. Quiz: Uji Pemahamanmu!

Setelah membaca tutorial di atas, jawablah 5 pertanyaan berikut untuk menguji pemahamanmu tentang Web Performance Optimization:

Pertanyaan 1: Apa metrik Core Web Vitals yang mengukur waktu render elemen terbesar?

a) CLS (Cumulative Layout Shift)
b) LCP (Largest Contentful Paint)
c) INP (Interaction to Next Paint)
d) FCP (First Contentful Paint)

Pertanyaan 2: Teknik apa yang menunda pemuatan resource hingga pengguna membutuhkannya?

a) Code Splitting
b) Minification
c) Tree Shaking
d) Lazy Loading

Pertanyaan 3: Format gambar apa yang menawarkan kompresi paling efisien untuk web modern?

a) PNG
b) JPEG
c) AVIF
d) BMP

Pertanyaan 4: Strategi caching apa yang menampilkan konten cache sambil mengupdate di background?

a) Cache-First
b) Network-Only
c) Stale-While-Revalidate
d) Network-First

Pertanyaan 5: Apa fungsi utama CDN (Content Delivery Network)?

a) Mengenkripsi data website
b) Menyimpan konten di server terdekat untuk mengurangi latency
c) Mengompresi kode JavaScript
d) Mengoptimalkan database queries
πŸ” Zoom
100%
🎨 Tema