Topik wawancara representatif

Wawancara Frontend: Bagaimana Anda Merancang Navigasi SPA yang Dapat Dipulihkan (Recoverable) dengan Navigation API?

FrontendSulit
Tim Redaksi Offer.ccDipublikasikan Diperbarui

Pertanyaan

Bagaimana Anda mendesain ulang lapisan navigasi SPA agar dapat dipulihkan (recoverable) dan dapat diamati (observable) tanpa merusak riwayat browser? Jelaskan loading race, pemulihan kesalahan, pemulihan scroll, dan fallback saat Navigation API tidak tersedia.

Pertanyaan dan konteks

Bagaimana Anda mendesain ulang lapisan navigasi SPA agar dapat dipulihkan (recoverable) dan dapat diamati (observable) tanpa merusak riwayat browser? Jelaskan loading race, pemulihan kesalahan, pemulihan scroll, dan fallback saat Navigation API tidak tersedia.

Pertanyaan ini cocok untuk posisi frontend, full-stack, dan platform web. Pertanyaan ini menguji model navigasi browser, asynchronous state machine, dan progressive enhancement alih-alih menghafal konfigurasi framework. Navigation API menyediakan tampilan terpadu dari navigasi same-document melalui event seperti navigate, navigatesuccess, dan navigateerror, ditambah intercept(). API ini tidak menggantikan pemuatan awal yang dirender server atau mengubah batas keamanan navigasi cross-document.

Apa yang dinilai pewawancara

  • Memperlakukan URL dan entri riwayat sebagai status navigasi, bukan hanya memori komponen.
  • Membedakan rute same-document dari dokumen lengkap, unduhan, formulir, dan tautan cross-origin.
  • Menangani permintaan usang (stale requests), pembatalan, kesalahan, serta navigasi mundur/maju.
  • Menentukan aturan pemulihan scroll, fokus, dan status halaman.
  • Menjaga pemuatan pertama dan browser yang tidak didukung tetap berfungsi.
  • Menginstrumentasikan keberhasilan, kegagalan, batas waktu (timeout), pembatalan, dan pemulihan.

Jawaban 30 detik

“Saya akan menjaga titik masuk yang dirender server dan tautan normal tetap berfungsi, dengan URL dan riwayat sebagai sumber kebenaran (source of truth). Ketika Navigation API tersedia, saya hanya akan mencegat rute aplikasi same-origin, memulai pemuatan yang dapat dibatalkan di event navigate, dan membiarkan navigasi yang lebih baru membatalkan navigasi yang lebih lama. Hanya tugas saat ini yang dapat menerapkan (commit) tampilan. Jika berhasil, saya memulihkan scroll dan fokus; jika gagal, saya mempertahankan tampilan lama dan menawarkan opsi coba lagi atau muat ulang. Browser yang tidak didukung menggunakan jalur History API yang ada, berbagi loader dan metrik yang sama.”

Solusi langkah demi langkah

Langkah 1: Batasi navigasi yang Anda cegat

Periksa apakah tujuan adalah same-origin, rute aplikasi, dan aman untuk dirender dalam dokumen saat ini. Tautan eksternal, unduhan, tujuan cross-origin, protokol khusus, dan semantik formulir harus mempertahankan perilaku default browser. Permintaan dokumen pertama tetap menjadi milik server dan browser; event navigate tidak dapat membuat pemuatan skrip yang gagal dapat dipulihkan dengan sendirinya.

Panggil intercept() hanya untuk rute aplikasi yang disetujui. Handler akan memuat data, memperbarui tampilan, dan menerapkan kebijakan scroll. Untuk setiap tujuan lainnya, izinkan browser bernavigasi secara normal. Ini mempertahankan semantik tautan dan menghindari perubahan navigasi platform menjadi state machine khusus klien yang tidak disengaja.

Langkah 2: Buat navigasi dapat dibatalkan

Tetapkan nomor urut yang terus bertambah untuk setiap navigasi dan berikan AbortSignal-nya ke loader data. Jika pengguna berpindah dari /search?q=a ke /search?q=ab, respons yang terlambat dari permintaan pertama tidak boleh menimpa permintaan kedua. Sebelum melakukan commit, periksa bahwa sinyal belum dibatalkan, urutan adalah yang terkini, dan tujuan masih cocok dengan status yang diinginkan.

js
let latestNavigation = 0;

navigation.addEventListener("navigate", (event) => {
  if (!event.canIntercept || !isAppRoute(event.destination.url)) return;
  const id = ++latestNavigation;
  event.intercept({
    async handler() {
      const data = await loadRoute(event.destination.url, event.signal);
      if (event.signal.aborted || id !== latestNavigation) return;
      renderRoute(data);
    }
  });
});

Contoh ini menunjukkan aturan race condition, bukan router lengkap. Kode produksi masih memerlukan penanganan kesalahan loader, batas waktu, cache hit, dan pelepasan komponen (component teardown). Prinsip “respons terakhir yang menang” tidak benar karena urutan penyelesaian jaringan bukanlah niat terbaru pengguna.

Langkah 3: Terapkan (commit) riwayat, scroll, dan status halaman

Setelah navigasi berhasil, tetapkan URL tujuan sebagai hasilnya. Jika nilai kecil yang dapat dipulihkan termasuk dalam entri riwayat saat ini, updateCurrentEntry() dapat menyimpannya; objek besar dan data sensitif tidak boleh disimpan di sana. Kebijakan scroll harus membedakan rute baru, navigasi mundur/maju, dan anchor: rute baru biasanya dimulai dari atas, sedangkan penelusuran riwayat memulihkan posisi yang disimpan.

Hindari mengubah judul, item yang dipilih, atau breadcrumb di awal pemuatan kecuali antarmuka pengguna (UI) secara eksplisit mewakili status tertunda (pending). Terapkan status visual utama setelah berhasil, dan pertahankan konten lama dengan tindakan coba lagi setelah gagal. navigatesuccess dan navigateerror adalah titik observasi yang berguna untuk metrik latensi, pembatalan, dan kegagalan.

Langkah 4: Pulihkan dari kesalahan

Halaman lama harus tetap dapat digunakan saat pemuatan gagal. Sediakan tindakan coba lagi, kembali, dan muat ulang penuh, serta bedakan antara autentikasi, otorisasi, sumber daya yang hilang, dan kegagalan jaringan sementara. Jika tujuan memerlukan respons dokumen lengkap, hentikan pencegatan dan biarkan browser menanganinya.

Jika status klien atau eksekusi skrip rusak, tautan normal dan rute server harus tetap dapat membangun kembali halaman dari URL. Jangan membuat pemulihan bergantung pada cache di memori. Catat tujuan, urutan navigasi, kelas kesalahan, dan apakah fallback terjadi sehingga masalah “alamat berubah tetapi halaman lama tetap ada” dapat didiagnosis.

Langkah 5: Terapkan progressive enhancement

Dukungan Navigation API lebih baru daripada baseline History API, sehingga tidak dapat menjadi satu-satunya jalur. Deteksi window.navigation dan metode yang sebenarnya Anda gunakan, lalu pilih jalur yang ditingkatkan. Browser yang tidak didukung harus menggunakan kembali loader rute yang sama melalui History API yang ada atau router framework; jangan menduplikasi aturan bisnis hanya demi kompatibilitas.

Uji pemuatan pertama, refresh, mundur/maju, tautan yang terputus, dan kegagalan skrip di kedua jalur kapabilitas. Sertakan jaringan lambat, navigasi cepat, tautan cross-origin, dan unduhan. Deteksi kapabilitas harus dilakukan dekat dengan penggunaan fitur daripada menggunakan daftar versi browser yang rentan usang.

Langkah 6: Verifikasi performa dan aksesibilitas

Gunakan tugas nyata: pencarian, pemfilteran, kembali, refresh, deep link, dan coba lagi. Ukur time to interactive, tingkat pembatalan, tingkat kegagalan, tingkat fallback, dan permintaan duplikat, yang dikelompokkan berdasarkan kapabilitas browser. Waktu render komponen saja tidak dapat menjelaskan kegagalan data, izin, atau riwayat.

Pertahankan semantik tautan asli, perilaku keyboard, dan perpindahan fokus yang aksesibel. Setelah navigasi yang dicegat, perbarui judul dokumen dan pindahkan fokus ke konten utama; jangan mencegah pembukaan tautan di tab baru. Caching dapat mengurangi latensi, tetapi tidak boleh menjadi satu-satunya sumber tempat halaman dapat dipulihkan.

Keuntungan informasi dan batasan

Perbedaan yang berguna adalah bahwa Navigation API menggabungkan event navigasi platform, riwayat, dan pemuatan yang dapat dibatalkan ke dalam satu rantai status. Ini tidak membuat sumber data menjadi andal atau mengubah halaman cross-origin menjadi rute same-document. Jawaban wawancara yang kuat menyatakan batasan pencegatan, titik commit, perilaku tampilan lama saat gagal, dan perilaku default saat browser tidak memiliki kapabilitas tersebut.

Contoh jawaban

“Saya akan mengklasifikasikan navigasi menjadi rute aplikasi same-origin, dokumen penuh, unduhan, formulir, dan tujuan cross-origin, serta hanya mencegat kelas pertama. Titik masuk server dan tautan biasa tetap valid, dengan URL dan riwayat sebagai sumber kebenaran. Dengan Navigation API, event navigate memanggil intercept() dan membuat nomor urut beserta sinyal pembatalan. Navigasi yang lebih baru membatalkan pemuatan lama, dan hasilnya hanya diterapkan jika masih merupakan yang terkini.

Setelah berhasil, saya memperbarui tampilan, judul, fokus, dan kebijakan scroll. Rute baru dimulai dari atas; navigasi mundur dan maju memulihkan posisi riwayat. Status kecil yang dapat dipulihkan dapat menggunakan entri riwayat saat ini, sementara data besar atau sensitif tetap disimpan di tempat lain. Jika gagal, saya mempertahankan tampilan lama, menawarkan opsi coba lagi, kembali, atau muat ulang penuh, serta mencatat latensi, pembatalan, dan kelas kesalahan melalui event navigasi dan log server.

Jika Navigation API tidak tersedia, loader yang sama berjalan melalui History API atau router framework; tautan eksternal dan unduhan selalu menggunakan perilaku default browser. Saya akan memverifikasi deep link, refresh, klik cepat, jaringan lambat, tautan cross-origin, kegagalan skrip, dan tugas teknologi asistif untuk memastikan alamat, konten, fokus, dan riwayat tidak pernah berselisih. Hasilnya adalah lapisan navigasi yang progresif alih-alih pengganti khusus klien yang hanya berfungsi di browser baru.”

Kesalahan umum

  • Mencegat setiap tautan → unduhan, formulir, dan semantik cross-origin rusak → cegat hanya rute aplikasi same-origin yang disetujui.
  • Membiarkan respons terakhir menang → data usang dapat menimpa niat pengguna saat ini → gunakan pembatalan dan pemeriksaan urutan.
  • Hanya menguji rendering → kegagalan data, izin, dan riwayat tetap tersembunyi → uji seluruh tugas navigasi dan pemulihannya.
  • Memperlakukan Navigation API sebagai infrastruktur first-load → refresh atau kegagalan skrip menjadi tidak dapat dipulihkan → pertahankan respons server dan tautan normal.
  • Menempatkan semua status di entri riwayat → entri menjadi terlalu besar atau mengekspos data sensitif → simpan hanya status kecil yang dapat direkonstruksi.
  • Menduplikasi logika bisnis untuk fallback → jalur yang ditingkatkan dan jalur fallback menyimpang → bagikan loader, aturan status, dan metrik yang sama.

Pertanyaan lanjutan

Bagaimana jika permintaan usang sudah mengisi cache?

Penulisan cache mungkin diizinkan, tetapi commit UI tetap memerlukan urutan terkini dan sinyal yang tidak dibatalkan. Jadikan URL, parameter, dan versi sebagai key entri. Hasil yang terlambat dapat menghangatkan (warm) permintaan di masa mendatang, tetapi tidak boleh mengubah halaman saat ini.

Haruskah kegagalan otorisasi kembali ke halaman sebelumnya atau mengarahkan ke halaman login?

Gunakan status server eksplisit untuk membedakan unauthenticated, unauthorized, dan sumber daya yang hilang. Pengguna yang belum terautentikasi dapat diarahkan ke login dengan URL kembali (return URL); pengguna yang tidak memiliki otorisasi memerlukan penjelasan dan tindakan aman berikutnya. Jangan menyamarkan masalah otorisasi sebagai kesalahan jaringan generik.

Bagaimana Anda menguji browser tanpa Navigation API?

Jalankan pengujian deep link, refresh, mundur/maju, jaringan lambat, dan kegagalan yang sama melalui deteksi kapabilitas, dengan memeriksa URL, konten, judul, fokus, dan scroll. Kesetaraan perilaku adalah hal yang penting; urutan event internal tidak harus identik.

Mengapa tidak mengandalkan router framework sepenuhnya?

Router framework dapat digunakan kembali, tetapi jawabannya harus mendefinisikan batas browser: navigasi mana yang tetap native, mana yang dapat ditingkatkan, dan bagaimana pembatalan, riwayat, serta kesalahan dipetakan ke dalam event framework. Mulailah dari semantik platform, lalu jelaskan adapternya.

Sumber publik

Pertanyaan terkait