Di 57 Performance Dasar, Metrik, dan Cara Ngukur > Praktik Performa Berkelanjutan kita lihat pola “death by a thousand cuts”: marketing nambah pixel, merchant install app, kampanye ninggalin script. Hampir semuanya pihak ketiga. Bab ini soal ngelawan itu, termasuk satu bagian yang agak gelap: app yang nipu alat pengukur.

Kode pihak ketiga itu bagian besar latensi storefront. Web Almanac 2022 ngelaporin median blocking time 1,4 detik buat 10 pihak ketiga paling populer, jadi halaman yang ngeload beberapa di antaranya bisa keblokir berdetik-detik sebelum paint. Dan beda dari kode theme, kode ini sering gak kamu tulis dan gak kamu kontrol, jadi pendekatannya lebih ke audit, evaluasi, dan negosiasi daripada optimasi baris kode.

App yang Ngeblokir Render

Resource render-blocking nyegah browser nampilin konten sampai resource itu di-download, di-parse, dan dieksekusi. Tiap app render-blocking nambah latensi ke critical rendering path. App pihak ketiga pelaku umum karena: script sinkron (tag <script> tanpa async/defer), CSS yang ngeblokir walaupun kontennya gak langsung kelihatan, banyak request HTTP terpisah, delivery gak teroptimasi (bukan dari CDN bagus), dan dependensi berantai (app ngeload resource lain secara sinkron).

1. Audit

Tiga alat:

  1. Lighthouse Performance audit → “Eliminate render-blocking resources”, catat semua script dan stylesheet pihak ketiga.
  2. WebPageTest waterfall → resource yang ngeblokir render (bar merah), catat domain pihak ketiga.
  3. Network panel → filter JS dan CSS, urutkan Waterfall, identifikasi domain vendor app.

Tipe app yang sering ngeblokir: chat (Intercom, Drift, Zendesk), review (Yotpo, Judge.me, Loox), email capture (Klaviyo, Privy, Justuno), analitik (GA, Facebook Pixel, Hotjar), A/B testing (Optimizely, VWO), rekomendasi (LimeSpot, Wiser, Nosto).

2. Evaluasi Kebutuhan

Buat tiap app render-blocking, tanya:

  1. Beneran dipakai? Cek metrik penggunaan di dashboard app, tanggal konfigurasi terakhir, nilai bisnisnya.
  2. Perlu di semua halaman? Bisa dibatasi ke halaman tertentu atau kondisional?
  3. Ada alternatif lebih ringan? Fitur native Shopify, solusi terintegrasi theme, app yang lebih performan.
  4. Bisa di-defer? Harus render segera atau bisa setelah paint awal?

Prioritas penghapusan:

  1. Tinggi (hapus segera): app gak/jarang dipakai, fungsi duplikat, punya alternatif native Shopify, blocking ≥ 500ms.
  2. Sedang (optimasi atau ganti): dipakai sesekali, punya opsi async, bisa kondisional.
  3. Rendah (optimasi aja): kritis buat bisnis, tanpa alternatif, blocking < 100ms.

3. Optimasi yang Tersisa

Defer app non-kritis sampai setelah load:

<script>
  // Defer app loading until after the page loads
  window.addEventListener('load', function() {
    // Load the chat widget after the page loads
    var script = document.createElement('script');
    script.src = 'https://widget.app.com/script.js';
    script.async = true;
    document.head.appendChild(script);
  });
</script>

Kondisional per tipe halaman:

{% if template.name == 'product' %}
  {{ 'review-app.css' | asset_url | stylesheet_tag }}
  <script src="https://reviews.app.com/widget.js" defer></script>
{% endif %}

Lazy-load app di luar viewport awal, dengan fallback timer:

<script>
  var emailAppLoaded = false;
  function loadEmailApp() {
    if (emailAppLoaded) return;
    emailAppLoaded = true;
    var script = document.createElement('script');
    script.src = 'https://email.app.com/popup.js';
    document.head.appendChild(script);
  }
  // Load on scroll
  window.addEventListener('scroll', loadEmailApp, { once: true });
  // Fallback: load after 5 seconds
  setTimeout(loadEmailApp, 5000);
</script>

Catatan: pola-pola ini cuma bisa diterapin ke script yang kamu kontrol tag-nya di theme. App yang nyuntik lewat app embed block (28 Blocks) dikontrol lewat toggle di theme editor, dan cara ngeload-nya tanggung jawab developer app.

Audit dan Hapus Script

Ini optimasi berdampak tertinggi buat responsivitas dan performa keseluruhan. Tiap script pihak ketiga nambah kerja main thread, dan efek kumulatifnya kena banyak CWV: INP (script yang jalan saat interaksi ngeblokir respons, bahkan yang di latar bersaing waktu), FCP (script blocking nunda paint pertama), LCP (script yang nunda discovery resource, tag manager yang nyuntik resource lain bikin rantai dependensi), TBT (long task).

1. Inventaris

Lighthouse Treemap buat visualisasi semua JavaScript per origin dengan ukuran byte. Coverage tab DevTools (More tools > Coverage): reload, script dengan persentase kode gak terpakai tinggi (bar merah = di-download tapi gak pernah dieksekusi) jadi kandidat. Tapi Coverage spesifik halaman yang direkam: script yang kelihatan gak terpakai di homepage bisa kerja beneran di halaman produk. Jalankan di template representatif (home, koleksi, produk) sebelum nyimpulin.

2. Verifikasi Masih Perlu

Cek sama marketing dan stakeholder. Sumber bloat umum: analitik ganda (banyak provider ngukur hal sama, tag manager yang ngeload tag duplikat analitik bawaan Shopify), A/B testing (JavaScript blocking buat semua pengunjung demi tes di sebagian kecil halaman), chat (ratusan KB tiap halaman walau jarang dipakai), review (nyuntik di semua halaman padahal cuma tampil di produk), email capture (semua halaman walau dikonfigurasi buat halaman tertentu), embed sosial (Instagram feed, Twitter widget bawa framework dan tracking sendiri), pixel marketing (retargeting, konversi, audience). Cari duplikat: dua analitik, banyak app dengan fungsi tumpang tindih.

3. Verifikasi Penghapusan Tuntas

Uninstall app dari admin gak selalu ngehapus kodenya dari theme. Banyak app nyuntik <script>, link stylesheet, atau snippet Liquid ke file theme waktu install. Setelah uninstall:

  1. Cari nama app atau domain script-nya di file theme, terutama layout/theme.liquid.
  2. Cek folder snippets/ buat file yang app bikin (sering ada nama app di nama file).
  3. Cari {% render %} atau {% include %} yang ngerujuk snippet app, di section atau layout.
  4. View source di browser, cari domain app-nya buat mastiin script-nya gak dimuat lagi.
{%- comment -%}
  This snippet was injected by an app that has since been uninstalled.
  The render tag in theme.liquid still loads it on every page.
  Safe to remove both the snippet file and the render tag.
{%- endcomment -%}
{% render 'old-app-tracking' %}

Cari file di snippets/ yang kamu gak kenal, cocokkan sama daftar app terinstal, hapus snippet dan tag render-nya. Ini juga alasan kenapa theme app extension (52 Online Store 2.0 dan Migrasinya > 3. App Block) diciptain: kodenya hilang waktu app di-uninstall.

4. Load Kondisional dan Trade-off

Script yang harus tetap ada: batasi di mana jalannya (review cuma di produk, chat cuma setelah scroll dan bukan di cart), pola dari 61 Performance JavaScript dan INP > defer dan async. Dan buat tiap yang tersisa, evaluasi biaya vs manfaat: bisa dua app serupa dikonsolidasi? Ada alternatif lebih ringan? Nilai bisnisnya sepadan biaya di tiap page load? Ini butuh keselarasan organisasi dari bab 57.

5. Ngukur Biaya Satu Domain

Pakai Network Request Blocking buat ngukur biaya script tertentu sebelum mutusin ngehapus: Network panel, catat total transfer dan jumlah request → tab Network request blocking (menu tiga titik, atau Command Menu “Show Network request blocking”) → tambahin pola *.analytics-provider.com → aktifkan → reload dan bandingin. Terus di Performance: rekam dengan domain diblokir, tab Bottom-Up, group by URL, bandingin total scripting. Ini angka konkret buat ngajuin penghapusan ke stakeholder.

Anti-flicker A/B Testing

Anti-flicker snippet itu JavaScript sinkron yang ngeblokir render dengan nyembunyiin konten sampai script testing dimuat, buat nyegah FOOC (Flash of Original Content: halaman asli muncul sebentar sebelum diganti varian; beda dari FOUC yang soal CSS). Ini bikin jeda buatan di critical rendering path, biayanya sering 2+ detik FCP dan langsung ngerusak LCP. Trade-off-nya: biaya performa vs akurasi tes vs nilai insight.

Yang paling sering terjadi: snippet-nya tetap ada walaupun gak ada tes yang jalan. Solusinya, theme setting yang ngehapus kodenya sepenuhnya:

{
  "name": "Third parties",
  "settings": [
    {
      "type": "checkbox",
      "id": "enable_vwo",
      "label": "Enable VWO Async SmartCode (for A/B testing)",
      "default": false
    }
  ]
}
{% if settings.enable_vwo %}
  <script>
    // VWO Async SmartCode
  </script>
{% endif %}

Setelah tes selesai: tutup tes di platform-nya (yang harusnya matiin anti-flicker otomatis) dan verifikasi. Dan pertimbangkan server-side A/B testing yang gak punya biaya anti-flicker karena konten datang udah dirender dengan varian yang benar.

Cara ngenalinnya di Network panel: HTML dan CSS download cepat, terus jeda panjang sebelum FCP, dengan domain kaya googleoptimize.com atau visualwebsiteoptimizer.com di antaranya. FCP harusnya terjadi sesaat setelah CSS dimuat.

❌ anti-flicker permanen, 2 detik FCP terbuang tiap hari tanpa tes

<script>/* anti-flicker: document.documentElement.style.opacity = 0 ... */</script>

✅ di balik setting, mati waktu gak ada tes

{% if settings.enable_ab_testing %}<script>/* anti-flicker */</script>{% endif %}

App Performa Palsu

Beberapa layanan “optimasi kecepatan” nipu alat tes performa: mendeteksi kapan alat itu jalan dan nyajiin halaman yang diubah biar skornya bagus tapi gak ngasih manfaat nyata. Pengguna nyata gak dapat apa-apa, dan performa bisa malah memburuk karena JavaScript obfuscated tambahan. Shopify udah ngehapus kebanyakan app begini dari App Store, tapi teknik yang sama sekarang dijual sebagai jasa “speed optimization” di platform freelance, dan kode yang mereka suntik pakai pola deteksi dan manipulasi yang sama.

Prinsipnya: Core Web Vitals itu proxy buat pengalaman, bukan pengalamannya sendiri. LCP ngedekati “konten utama kelihatan”, INP ngedekati “halaman merespons waktu disentuh”. Proxy bisa dipenuhi tanpa hal yang diwakilinya membaik, dan celah itulah yang dieksploitasi. Jadi kenaikan skor gak pernah cukup sebagai bukti. Perubahan yang beneran memperbaiki pengalaman hampir selalu kelihatan sebagai piksel yang muncul lebih cepat atau interaksi yang merespons lebih cepat, dan kamu bisa lihat keduanya di filmstrip. Perubahan yang cuma nggerakin angka ninggalin filmstrip dan timing interaksi sama seperti sebelumnya.

1. Tiga Cara Nipu

  1. LCP hijacking: browser gak menilai apakah elemen LCP bermakna, elemen apapun yang cukup besar dan paint cukup cepat jadi kandidat. Script nyuntik elemen yang paint hampir seketika dan nutupin viewport: gambar transparan 1 piksel yang diskalain penuh, div full-viewport dengan warna latar, atau blok teks besar di luar layar tapi tetap dihitung paint. Browser nyatat itu sebagai LCP dan berhenti mempromosikan kandidat berikutnya, jadi hero gak lagi berkontribusi ke metrik. Gak ada yang berubah soal loading hero-nya. Dan karena elemen ini disajikan ke semua orang, RUM juga ngelaporin angka “membaik” yang sama, jadi field dan lab sepakat, bikin manipulasinya kelihatan kaya kemenangan asli.
  2. Deteksi alat tes: varian yang lebih rumit nyajiin dua halaman berbeda: yang dipreteli ke alat tes, yang asli ke pengguna nyata, dengan fingerprinting klien di atas dokumen. Sinyalnya murah: Lighthouse ngenalin diri dengan Chrome-Lighthouse di user agent dan mengemulasi Moto G, jadi tes substring Chrome-Lighthouse, GTmetrix, atau Moto G Power nangkep kebanyakan lab run. Alat tes hosted jalan di Linux sementara hampir semua pembeli di Android/iOS/Windows/macOS, jadi navigator.platform = Linux x86_64 itu penanda kuat. Ada juga cek touch support, artefak headless, navigator.plugins kosong. Kalau cocok, script ngelewatin tag pihak ketiga, ngehapus gambar, atau memotong JavaScript theme. Ini varian paling merusak: angka yang kamu lihat menggambarkan halaman yang gak pernah diterima pembeli manapun.
  3. Manipulasi resource: gak bikin resource lebih murah, tapi bikin datang setelah jendela pengukuran tutup. Script nge-patch API browser yang theme andalkan, terus ngelepas kerjanya belakangan: nimpa IntersectionObserver atau perilaku loading="lazy" biar gambar gak pernah “masuk viewport”, ngeganti document.createElement/appendChild buat nyegat tag script, nahan tag pihak ketiga di antrean yang cuma dikuras di scroll/mousemove/touch pertama. Alat lab jarang berinteraksi, jadi antrean gak pernah dikuras selama tes dan biaya yang ditunda gak kelihatan. Varian paling destruktif pakai document.write()/document.open()/document.close() buat ngosongin dokumen waktu tes terdeteksi, ngasilin metrik hampir sempurna buat halaman tanpa konten. Di kunjungan nyata, intersepsi yang sama nunda hydration sampai setelah pembeli nyoba tap sesuatu: INP buruk dan kontrol yang kelihatan siap tapi gak merespons.

2. Bandingin Skor Antar Alat

  1. Jalankan PageSpeed Insights.
  2. Jalankan Lighthouse di DevTools (Incognito).
  3. Beda 20+ poin → kemungkinan besar kena.
  4. Cek screenshot PageSpeed Insights: halaman kelihatan rusak → kena.
  5. Periksa diagnostik Largest Contentful Paint element: kelihatan artifisial → metriknya dicurangi.
  6. Cek Web Performance Dashboard: pengguna nyata biasanya lebih cepat dari lab, bukan lebih lambat.

3. Konfirmasi Mekanismenya di DevTools

Apakah kandidat LCP berubah, atau piksel datang lebih cepat? Performance panel dengan Screenshots dan throttling mobile, rekam reload, pilih marker LCP, tab Summary nyebut elemennya. Inspect di Elements: gambar transparan, div kosong, overlay, atau apapun yang pembeli gak bakal sebut “konten utama” → metriknya ngukur elemen yang disuntik. Scrub filmstrip ke timestamp LCP dan bandingin sama frame di mana hero beneran kelihatan. Hero muncul jauh setelah LCP yang dilaporin → angkanya membaik tanpa halaman jadi lebih cepat. Jalankan sebelum dan sesudah matiin kode yang dicurigai: optimasi asli nggerakin frame hero muncul, manipulasi cuma nggerakin marker.

Apakah ada sniffing alat tes? Tab Network conditions, matiin “Use browser default” di User agent, masukin string berisi Chrome-Lighthouse, reload dan catat request. Balikin default, reload, catat lagi. Request yang cuma muncul di default, atau hilang di string Lighthouse, berarti halaman bercabang berdasarkan siapa yang ngukur. Halaman yang jujur nyajiin resource yang sama ke keduanya. Terus Search drawer (nyari di semua resource yang dimuat): Chrome-Lighthouse, Linux x86_64, Moto G, navigator.platform, termasuk bentuk escaped karena biasanya di-obfuscate: \x4c\x69\x6e\x75\x78\x20\x78\x38\x36\x5f\x36\x34.

Apakah hydration ditunda sampai interaksi? Load halaman, jangan sentuh, biarin Network panel tenang. Gerakin mouse/scroll/tap sekali, perhatiin: ledakan request script di input pertama = kerja ditunda melewati jendela pengukuran, bukan dihilangkan. Segera setelah halaman kelihatan siap, coba pakai: buka cart drawer, ganti varian, buka menu. Kontrol yang ter-paint tapi gak responsif = metrik selesai sebelum halaman bisa dipakai. Bandingin INP lab vs field: field yang jauh lebih buruk dari lab itu sinyal kuat. Nunda kerja itu teknik sah (dari bab 61), jadi butuh pertimbangan: nunda chat widget = perbaikan nyata, nunda pemilih varian theme sendiri = nggerakin metrik dan bikin halaman lebih buruk.

4. Theme Check dan Penghapusan

Gak ada aturan Theme Check yang ngedeteksi manipulasi metrik: Theme Check menganalisis pola yang dikenal dan gak bisa nalar apakah script lagi ngakalin pengukuran, dan kode begini sengaja di-obfuscate. Deteksinya manual. Dua aturan bisa nunjukin titik suntikan karena payload-nya hampir selalu dari domain sendiri: RemoteAsset (aset dari domain pihak ketiga, selidiki domain yang gak dikenal) dan ParserBlockingScript (fingerprinting harus jalan sebelum render, jadi sering parser-blocking). Keduanya bukan bukti, cuma daftar pendek buat dibaca.

Penghapusan: audit snippets/social-meta-tags.liquid, snippets/meta-tags.liquid, layout/theme.liquid, cari JavaScript obfuscated; cari pola "Linux x86_64" atau hex-nya, "Lighthouse", "GTmetrix", "Moto G", gambar/div transparan fullscreen, document.write()/open()/close(); hapus di theme duplikat dulu, verifikasi halaman masih berfungsi, dan konfirmasi kodenya beneran jahat karena ada pemakaian sah.

Reverse Proxy di Depan Shopify

Arahkan domain storefront langsung ke Shopify, bukan lewat CDN lain, WAF, atau middleware kustom. Tiap lapisan di depan edge Shopify itu satu hop tambahan yang browser bayar buat kerja yang Shopify udah lakuin dekat pembeli. Proxy nambah latensi ke tiap resource (dokumen HTML, panggilan API, aset yang dirutekan), yang langsung ngaruh ke TTFB dan LCP (dan FCP kalau aset ikut).

Proxy yang dipakai buat cache bikin masalah sendiri: HTML cache bisa nampilin data produk basi, inventaris salah, harga lama, invalidasi cache susah dan kegagalannya sering diam, dan RUM dari respons cache gak mencerminkan performa waktu cache miss. Shopify udah ngejalanin CDN global dengan caching, kompresi, TLS, dan distribusi geografis, jadi proxy tambahan duplikasi.

1. Deteksi

Tiga cek terhadap domain kustom dan origin .myshopify.com, bandingin pasangannya:

  1. DNS: setup resmi Shopify ngarahin apex ke IP Shopify dan www ke shops.myshopify.com. Ikuti rantai CNAME, bukan cuma alamat akhir:
# The full CNAME chain for the storefront hostname
dig +noall +answer www.example.com
# Who runs the zone
dig +short NS example.com

CNAME ke hostname layanan proxy, atau nameserver milik CDN, berarti request lewat layanan itu dulu. 2. Header respons (Network panel di request dokumen awal, atau curl -sSI), bedanya yang jadi sinyal, bukan satu header tertentu: server yang nyebut proxy, via (nyebut perantara eksplisit), x-served-by/x-cache/x-proxy-cache/age yang cuma ada di domain kustom, atau dua lapis header yang sama (x-cache dobel, via bersarang):

curl -sSI https://www.example.com/collections/all > proxied.txt
curl -sSI https://example.myshopify.com/collections/all > origin.txt
diff origin.txt proxied.txt
  1. TTFB: sampel beberapa kali dari jaringan yang sama, bandingin median:
for host in www.example.com example.myshopify.com; do
  echo "$host"
  for i in $(seq 1 10); do
    curl -sS -o /dev/null \
      -w '%{time_starttransfer}\n' \
      "https://$host/collections/all"
  done | sort -n | awk 'NR == 5 {print "  median", $1}'
done

Interpretasinya: DNS pihak ketiga tapi header identik dan TTFB sama → zona di-host di luar tapi record gak di-proxy (“DNS only”), gak ada yang perlu dihapus. DNS pihak ketiga + lapisan cache kedua + TTFB konsisten lebih tinggi → proxy ada di jalur dan biayanya sebesar selisih itu tiap request. Lapisan cache kedua tapi TTFB dekat → proxy tetap di jalur, ukur request cart dan Section Rendering API sebelum nyimpulin gak berbahaya.

2. Kasus Cloudflare

Cloudflare itu proxy paling umum dan paling susah dideteksi dari header, karena CDN Shopify sendiri Cloudflare-backed: cf-ray, cf-cache-status, server: cloudflare ada di respons Shopify normal dan gak membuktikan apa-apa. Bedainnya: bandingin sama origin (zona Cloudflare milik merchant biasanya ngasilin dua cf-ray, cf-cache-status dobel, atau age/via tambahan), cek nameserver (Cloudflare NS = zona dikelola di akun Cloudflare, prasyarat proxying), dan di dashboard Cloudflare record yang Proxied vs DNS only. Buat storefront Shopify, DNS only yang bener.

Kalau zonanya di-proxy, tinjau juga fitur modifikasi konten Cloudflare: minifikasi dan optimasi gambar duplikat kerja Shopify tanpa manfaat, dan fitur penulisan ulang script lebih berisiko karena ngubah cara dan waktu JavaScript theme eksekusi, bisa ngerusak module loading dan nunda interaktivitas walaupun metrik dokumen kelihatan sama.

Peringatan soal cache hit rate: itu biasanya hal yang salah buat dioptimasi. Hit rate tinggi di HTML koleksi dan produk gak bantu request yang paling penting: cart, sesi, dan checkout bawa cookie dan gak bisa dilayani dari cache bersama. Tiap update /cart, Section Rendering API, dan add-to-cart lewatin cache proxy dan bayar hop penuh, dan itu tepat interaksi di mana pembeli ngerasain latensi.

3. Yang Harus Dilakuin

Kalau proxy cuma buat cache/CDN, redundan, hapus dengan ngarahin DNS langsung ke Shopify. Kalau buat fungsi spesifik (WAF, manipulasi header, A/B testing), evaluasi apakah fungsinya sepadan latensi tiap request, karena sering fungsi yang sama tersedia lewat app, kode theme, atau fitur bawaan Shopify. Kalau harus tetap ada (compliance, keamanan): rutekan cuma dokumen HTML, biarin aset langsung dari CDN Shopify; TTL cache HTML ≤ 5 menit; monitor hit rate dan kegagalan invalidasi; bandingin RUM cached vs uncached.

Dan ingat: proxy sering diadopsi buat menutupi page load lambat, bukan memperbaikinya. Atasi akar masalahnya: TTFB lambat → Liquid (58 Performance Liquid), bundle JavaScript besar → audit script di atas, gambar lambat → responsif dari CDN Shopify (59 Performance Gambar dan LCP), banyak domain aset → semua dari CDN Shopify (64 Performance Navigasi dan Loading).

Checkout UI Extension

Buat merchant Shopify Plus yang pakai checkout UI extension (pengganti checkout.liquid dari 34 checkout.liquid (deprecated)): extension dimuat sebagai bundle JavaScript di tiap pageview checkout, termasuk yang terinstal tapi gak ditugaskan ke surface aktif manapun. Extension yang gak dipakai tetap inisialisasi saat load walau gak ngerender apa-apa, jadi tetap bayar download, parse, dan eksekusi, paling besar di CPU mobile.

Elemen LCP checkout gak tersedia dari RUM. Yang ada: instrumentasi checkout nyatat dua timing per extension: extension_visible (sampai kontennya kelihatan) dan extension_buyer_usable (sampai bisa nerima input). extension_visible yang relevan buat LCP; extension bisa kelihatan dan memicu LCP tanpa bisa dipakai.

1. Nemuin yang Paling Lambat

Kerja di checkout nyata, bukan editor checkout (yang ngeload tooling ekstra): tambahin produk, buka checkout di Incognito (biar extension gak dari cache hangat), Network panel dengan Disable cache dan Slow 4G, filter extensions, urutkan Size lalu Time, aktifkan kolom Priority. Rekam Performance panel sampai checkout full render, pilih marker LCP: kalau elemennya milik block extension, itu bottleneck-nya. Cek long task di Main track setelah skeleton checkout paint, URL script di atas flame chart nunjukin extension mana. Ulangi di emulasi mobile dan ponsel nyata karena peringkatnya bisa beda. Urutkan berdasarkan waktu render konten yang kelihatan, bukan ukuran bundle: bundle kecil yang nunggu request jaringan bisa render lebih telat dari bundle besar yang langsung render.

Tabel cepat di Console:

console.table(
  performance
    .getEntriesByType('resource')
    .filter((entry) => entry.name.includes('/extensions/'))
    .map((entry) => ({
      asset: entry.name.split('?')[0].split('/').slice(-2).join('/'),
      transferKB: Math.round(entry.transferSize / 1024),
      decodedKB: Math.round(entry.decodedBodySize / 1024),
      startedAtMs: Math.round(entry.startTime),
      durationMs: Math.round(entry.duration),
    }))
    .sort((first, second) => second.decodedKB - first.decodedKB),
);

transferKB bisa 0 buat aset cross-origin tanpa timing header (bandingin decodedKB), dan aset yang di-fetch di dalam sandbox extension gak muncul di timeline dokumen utama (fallback ke Network panel). Rekam sebelum dan sesudah tiap perubahan, itu bukti termurah buat developer app.

2. Triase dan Tindakan

  1. Konten telat muncul dan LCP milik extension: perbaiki/hapus, atau minta developer render placeholder ringan sebelum fetch data.
  2. Konten cepat tapi interaksi gak responsif: masalah main thread setelah render (INP), ukur sambil berinteraksi, bukan saat load.
  3. Konten gak pernah muncul: gagal mount, tetap bayar biaya tanpa manfaat, hapus.
  4. Block di luar viewport awal (upsell di order status) gak bisa jadi LCP, deprioritaskan walau bundle-nya besar.

Hapus extension tanpa surface aktif (Settings > Checkout > Customize), konsolidasi yang tumpang tindih (dua trust badge, banyak upsell), dan libatkan developer dengan data timing (fase mana: download, parse/compile, atau render), karena bundle dan logika render itu kontrol mereka. Shopify gak publikasi batas ukuran bundle, jadi bandingin antar bundle di checkout kamu sendiri dan angkat outlier-nya. Penempatan ditentuin target di shopify.extension.toml developer: extension yang deklarasi target checkout dan Thank you jalan di keduanya, jadi kalau cuma butuh post-purchase, minta developer hapus target checkout-nya.

Ulangi audit tiap kuartal dan setelah install/hapus app checkout: semua extension punya surface aktif, gak ada dua yang sama fungsi, gak ada target checkout buat yang nilainya cuma di Thank you/Order status, dan gak ada capability network_access kecuali beneran butuh data eksternal saat checkout (panggilan eksternal = dependensi serial sebelum konten render).

Ringkasan

  1. App render-blocking: audit lewat Lighthouse/WebPageTest/Network, evaluasi (dipakai? semua halaman? alternatif? bisa defer?), prioritas hapus (≥500ms, duplikat, gak dipakai, ada native) → optimasi → biarin (kritis, <100ms). Defer setelah load, kondisional per template.name, lazy di scroll dengan fallback timer.
  2. Audit script (dampak tertinggi): Treemap + Coverage di beberapa template, verifikasi sama stakeholder (analitik ganda, A/B, chat, review, email, embed sosial, pixel), uninstall gak ngehapus kode (cari di theme.liquid, snippets/, render, view source), load kondisional, ukur biaya per domain lewat Network Request Blocking + Bottom-Up by URL.
  3. Anti-flicker A/B testing ngeblokir render 2+ detik: taruh di balik theme setting default: false, tutup tes yang selesai, pertimbangkan server-side.
  4. App performa palsu: LCP hijacking (elemen transparan fullscreen, field dan lab sepakat), deteksi alat tes (Chrome-Lighthouse, Linux x86_64, Moto G), manipulasi resource (patch API, antrean sampai interaksi, document.write). Cek beda skor 20+ antar alat, screenshot rusak, elemen LCP artifisial; konfirmasi lewat filmstrip vs marker, UA spoof, ledakan request di input pertama, INP field ≫ lab. Theme Check gak bisa deteksi (cuma RemoteAsset/ParserBlockingScript buat titik suntikan), hapus manual di theme duplikat.
  5. Reverse proxy nambah hop ke tiap request (TTFB, LCP), cache HTML bikin data basi: deteksi lewat dig (CNAME harus ke shops.myshopify.com, NS), diff header vs origin (via, cf-ray dobel, age), median TTFB; Cloudflare harus DNS only; hit rate bukan ukuran karena cart/sesi/checkout gak bisa di-cache; hapus atau batasi ke HTML dengan TTL ≤5 menit; perbaiki akar masalahnya.
  6. Checkout UI extension (Plus) dimuat tiap checkout termasuk yang gak dipakai: ukur extension_visible (LCP) vs extension_buyer_usable (INP) di checkout nyata dengan Slow 4G, tabel performance.getEntriesByType('resource'), hapus yang tanpa surface/gagal mount, konsolidasi, minta developer placeholder atau hapus target shopify.extension.toml, audit kuartalan.

Bab selanjutnya kita tutup bagian performa dengan resource hint dan navigasi: preconnect buat domain pihak ketiga, preload yang hemat, Speculation Rules buat prefetch/prerender halaman berikutnya, semua aset dari CDN Shopify, dan defer resource non-kritis.