Pemilihan rute sisi klien modern: Navigation API

Menstandardisasi perutean sisi klien melalui API baru yang sepenuhnya merombak pembuatan aplikasi halaman tunggal.

Browser Support

  • Chrome: 102.
  • Edge: 102.
  • Firefox: 147.
  • Safari: 26.2.

Source

Aplikasi web satu halaman, atau SPA, ditentukan oleh fitur inti: menulis ulang kontennya secara dinamis saat pengguna berinteraksi dengan situs, bukan metode default untuk memuat halaman yang sepenuhnya baru dari server.

Meskipun SPA dapat menghadirkan fitur ini melalui History API (atau dalam kasus terbatas, dengan menyesuaikan bagian #hash situs), API ini adalah API yang rumit yang dikembangkan jauh sebelum SPA menjadi norma—dan web sangat membutuhkan pendekatan yang benar-benar baru. Navigation API adalah API yang diusulkan untuk sepenuhnya merombak ruang ini, bukan hanya mencoba menambal kekurangan History API. (Misalnya, Scroll Restoration menambal History API, bukan mencoba membuatnya kembali.)

Postingan ini menjelaskan Navigation API secara garis besar. Untuk membaca proposal teknis, lihat Draf Laporan di repositori WICG.

Contoh penggunaan

Untuk menggunakan Navigation API, mulailah dengan menambahkan pemroses "navigate" pada objek navigation global. Peristiwa ini pada dasarnya terpusat: peristiwa ini akan diaktifkan untuk semua jenis navigasi, baik saat pengguna melakukan tindakan (seperti mengklik link, mengirimkan formulir, atau kembali dan maju) maupun saat navigasi dipicu secara terprogram (yaitu, melalui kode situs Anda). Dalam sebagian besar kasus, tindakan ini memungkinkan kode Anda mengganti perilaku default browser untuk tindakan tersebut. Untuk SPA, hal itu kemungkinan berarti mempertahankan pengguna di halaman yang sama dan memuat atau mengubah konten situs.

NavigateEvent diteruskan ke pemroses "navigate" yang berisi informasi tentang navigasi, seperti URL tujuan, dan memungkinkan Anda merespons navigasi di satu tempat terpusat. Pemroses "navigate" dasar dapat terlihat seperti ini:

navigation.addEventListener('navigate', navigateEvent => {
  // Exit early if this navigation shouldn't be intercepted.
  // The properties to look at are discussed later in the article.
  if (shouldNotIntercept(navigateEvent)) return;

  const url = new URL(navigateEvent.destination.url);

  if (url.pathname === '/') {
    navigateEvent.intercept({handler: loadIndexPage});
  } else if (url.pathname === '/cats/') {
    navigateEvent.intercept({handler: loadCatsPage});
  }
});

Anda dapat menangani navigasi dengan salah satu dari dua cara berikut:

  • Memanggil intercept({ handler }) (seperti yang dijelaskan di atas) untuk menangani navigasi.
  • Memanggil preventDefault(), yang dapat membatalkan navigasi sepenuhnya.

Contoh ini memanggil intercept() pada peristiwa. Browser memanggil callback handler Anda, yang akan mengonfigurasi status berikutnya dari situs Anda. Tindakan ini akan membuat objek transisi, navigation.transition, yang dapat digunakan kode lain untuk melacak progres navigasi.

intercept() dan preventDefault() biasanya diizinkan, tetapi ada kasus ketika keduanya tidak dapat dipanggil. Anda tidak dapat menangani navigasi melalui intercept() jika navigasi adalah navigasi lintas origin. Selain itu, Anda tidak dapat membatalkan navigasi melalui preventDefault() jika pengguna menekan tombol Kembali atau Maju di browsernya; Anda tidak boleh menjebak pengguna di situs Anda. (Hal ini sedang dibahas di GitHub.)

Meskipun Anda tidak dapat menghentikan atau mencegat navigasi itu sendiri, peristiwa "navigate" akan tetap dipicu. Hal ini informatif, sehingga kode Anda dapat, misalnya, mencatat peristiwa Analytics untuk menunjukkan bahwa pengguna meninggalkan situs Anda.

Mengapa perlu menambahkan acara lain ke platform?

Pemroses peristiwa "navigate" memusatkan penanganan perubahan URL di dalam SPA. Hal ini merupakan proposisi yang sulit menggunakan API lama. Jika pernah menulis perutean untuk SPA Anda sendiri menggunakan History API, Anda mungkin telah menambahkan kode seperti ini:

function updatePage(event) {
  event.preventDefault(); // we're handling this link
  window.history.pushState(null, '', event.target.href);
  // TODO: set up page based on new URL
}
const links = [...document.querySelectorAll('a[href]')];
links.forEach(link => link.addEventListener('click', updatePage));

Hal ini tidak masalah, tetapi tidak lengkap. Link dapat muncul dan hilang di halaman Anda, dan link bukan satu-satunya cara pengguna dapat menavigasi halaman. Misalnya, mereka dapat mengirimkan formulir atau bahkan menggunakan peta gambar. Halaman Anda mungkin menangani hal ini, tetapi ada banyak kemungkinan yang dapat disederhanakan—sesuatu yang dicapai oleh Navigation API baru.

Selain itu, kode di atas tidak menangani navigasi kembali/maju. Ada acara lain untuk itu, "popstate".

Secara pribadi, History API sering kali terasa dapat membantu mewujudkan kemungkinan ini. Namun, sebenarnya hanya memiliki dua area permukaan: merespons jika pengguna menekan Kembali atau Maju di browser, serta mengirim dan mengganti URL. Tidak memiliki analogi dengan "navigate", kecuali jika Anda secara manual menyiapkan pemroses untuk peristiwa klik, misalnya, seperti yang ditunjukkan di atas.

Menentukan cara menangani navigasi

navigateEvent berisi banyak informasi tentang navigasi yang dapat Anda gunakan untuk memutuskan cara menangani navigasi tertentu.

Properti utamanya adalah:

canIntercept
Jika nilainya salah (false), Anda tidak dapat mencegat navigasi. Navigasi lintas origin dan traversal lintas dokumen tidak dapat dicegat.
destination.url
Mungkin informasi terpenting yang perlu dipertimbangkan saat menangani navigasi.
hashChange
Benar jika navigasinya adalah dokumen yang sama, dan hash adalah satu-satunya bagian URL yang berbeda dengan URL saat ini. Di SPA modern, hash harus digunakan untuk menautkan ke berbagai bagian dokumen saat ini. Jadi, jika hashChange benar (true), Anda mungkin tidak perlu mengintersep navigasi ini.
downloadRequest
Jika benar, navigasi dimulai oleh link dengan atribut download. Dalam sebagian besar kasus, Anda tidak perlu mencegatnya.
formData
Jika ini bukan null, maka navigasi ini adalah bagian dari pengiriman formulir POST. Pastikan Anda mempertimbangkan hal ini saat menangani navigasi. Jika Anda hanya ingin menangani navigasi GET, hindari mencegat navigasi saat formData tidak null. Lihat contoh penanganan pengiriman formulir di bagian selanjutnya dalam artikel ini.
navigationType
Ini adalah salah satu dari "reload", "push", "replace", atau "traverse". Jika "traverse", maka navigasi ini tidak dapat dibatalkan melalui preventDefault().

Misalnya, fungsi shouldNotIntercept yang digunakan dalam contoh pertama dapat berupa seperti ini:

function shouldNotIntercept(navigationEvent) {
  return (
    !navigationEvent.canIntercept ||
    // If this is just a hashChange,
    // just let the browser handle scrolling to the content.
    navigationEvent.hashChange ||
    // If this is a download,
    // let the browser perform the download.
    navigationEvent.downloadRequest ||
    // If this is a form submission,
    // let that go to the server.
    navigationEvent.formData
  );
}

Menyadap

Saat kode Anda memanggil intercept({ handler }) dari dalam pendengar "navigate"-nya, kode tersebut akan memberi tahu browser bahwa kode tersebut sedang menyiapkan halaman untuk status baru yang diperbarui, dan navigasi mungkin memerlukan waktu beberapa saat.

Browser memulai dengan merekam posisi scroll untuk status saat ini, sehingga dapat dipulihkan nanti jika perlu, lalu memanggil callback handler Anda. Jika handler Anda menampilkan promise (yang terjadi secara otomatis dengan fungsi asinkron), promise tersebut memberi tahu browser durasi navigasi, dan apakah berhasil atau tidak.

navigation.addEventListener('navigate', navigateEvent => {
  if (shouldNotIntercept(navigateEvent)) return;
  const url = new URL(navigateEvent.destination.url);

  if (url.pathname.startsWith('/articles/')) {
    navigateEvent.intercept({
      async handler() {
        const articleContent = await getArticleContent(url.pathname);
        renderArticlePage(articleContent);
      },
    });
  }
});

Dengan demikian, API ini memperkenalkan konsep semantik yang dipahami browser: navigasi SPA sedang terjadi, dari waktu ke waktu, mengubah dokumen dari URL dan status sebelumnya ke yang baru. Hal ini memiliki sejumlah potensi manfaat, termasuk aksesibilitas: browser dapat menampilkan awal, akhir, atau potensi kegagalan navigasi. Misalnya, Chrome mengaktifkan indikator pemuatan native-nya, dan memungkinkan pengguna berinteraksi dengan tombol berhenti. (Saat ini, hal ini tidak terjadi saat pengguna menavigasi melalui tombol kembali/maju, tetapi akan segera diperbaiki.)

Saat mencegat navigasi, URL baru akan berlaku tepat sebelum callback handler Anda dipanggil. Jika Anda tidak segera memperbarui DOM, hal ini akan menciptakan periode saat konten lama ditampilkan bersama dengan URL baru. Hal ini memengaruhi hal-hal seperti resolusi URL relatif saat mengambil data atau memuat subresource baru.

Cara untuk menunda perubahan URL sedang didiskusikan di GitHub, tetapi umumnya disarankan untuk segera memperbarui halaman dengan beberapa jenis placeholder untuk konten yang masuk:

navigation.addEventListener('navigate', navigateEvent => {
  if (shouldNotIntercept(navigateEvent)) return;
  const url = new URL(navigateEvent.destination.url);

  if (url.pathname.startsWith('/articles/')) {
    navigateEvent.intercept({
      async handler() {
        // The URL has already changed, so quickly show a placeholder.
        renderArticlePagePlaceholder();
        // Then fetch the real data.
        const articleContent = await getArticleContent(url.pathname);
        renderArticlePage(articleContent);
      },
    });
  }
});

Hal ini tidak hanya menghindari masalah penyelesaian URL, tetapi juga terasa cepat karena Anda langsung merespons pengguna.

Sinyal pembatalan

Karena Anda dapat melakukan pekerjaan asinkron di handler intercept(), navigasi dapat menjadi berlebihan. Hal ini terjadi jika:

  • Pengguna mengklik link lain, atau beberapa kode melakukan navigasi lain. Dalam hal ini, navigasi lama ditinggalkan dan diganti dengan navigasi baru.
  • Pengguna mengklik tombol 'berhenti' di browser.

Untuk mengatasi kemungkinan ini, peristiwa yang diteruskan ke pemroses "navigate" berisi properti signal, yang merupakan AbortSignal. Untuk mengetahui informasi selengkapnya, lihat Pengambilan yang dapat dibatalkan.

Singkatnya, objek ini pada dasarnya menyediakan objek yang memicu peristiwa saat Anda harus menghentikan pekerjaan. Khususnya, Anda dapat meneruskan AbortSignal ke panggilan apa pun yang Anda lakukan ke fetch(), yang akan membatalkan permintaan jaringan dalam proses jika navigasi didahului. Hal ini akan menghemat bandwidth pengguna, dan menolak Promise yang ditampilkan oleh fetch(), sehingga mencegah kode berikutnya dari tindakan seperti memperbarui DOM untuk menampilkan navigasi halaman yang kini tidak valid.

Berikut adalah contoh sebelumnya, tetapi dengan getArticleContent yang disisipkan, yang menunjukkan cara penggunaan AbortSignal dengan fetch():

navigation.addEventListener('navigate', navigateEvent => {
  if (shouldNotIntercept(navigateEvent)) return;
  const url = new URL(navigateEvent.destination.url);

  if (url.pathname.startsWith('/articles/')) {
    navigateEvent.intercept({
      async handler() {
        // The URL has already changed, so quickly show a placeholder.
        renderArticlePagePlaceholder();
        // Then fetch the real data.
        const articleContentURL = new URL(
          '/get-article-content',
          location.href
        );
        articleContentURL.searchParams.set('path', url.pathname);
        const response = await fetch(articleContentURL, {
          signal: navigateEvent.signal,
        });
        const articleContent = await response.json();
        renderArticlePage(articleContent);
      },
    });
  }
});

Penanganan scroll

Saat Anda intercept() navigasi, browser akan mencoba menangani scrolling secara otomatis.

Untuk navigasi ke entri histori baru (jika navigationEvent.navigationType adalah "push" atau "replace"), ini berarti mencoba men-scroll ke bagian yang ditunjukkan oleh fragmen URL (bagian setelah #), atau mereset scroll ke bagian atas halaman.

Untuk pemuatan ulang dan traversal, hal ini berarti memulihkan posisi scroll ke tempat terakhir kali entri histori ini ditampilkan.

Secara default, hal ini terjadi setelah promise yang ditampilkan oleh handler Anda selesai, tetapi jika masuk akal untuk men-scroll lebih awal, Anda dapat memanggil navigateEvent.scroll():

navigation.addEventListener('navigate', navigateEvent => {
  if (shouldNotIntercept(navigateEvent)) return;
  const url = new URL(navigateEvent.destination.url);

  if (url.pathname.startsWith('/articles/')) {
    navigateEvent.intercept({
      async handler() {
        const articleContent = await getArticleContent(url.pathname);
        renderArticlePage(articleContent);
        navigateEvent.scroll();

        const secondaryContent = await getSecondaryContent(url.pathname);
        addSecondaryContent(secondaryContent);
      },
    });
  }
});

Atau, Anda dapat memilih untuk tidak menggunakan penanganan scroll otomatis sepenuhnya dengan menyetel opsi scroll dari intercept() ke "manual":

navigateEvent.intercept({
  scroll: 'manual',
  async handler() {
    // …
  },
});

Penanganan fokus

Setelah promise yang ditampilkan oleh handler Anda diselesaikan, browser akan memfokuskan elemen pertama dengan setelan atribut autofocus, atau elemen <body> jika tidak ada elemen yang memiliki atribut tersebut.

Anda dapat memilih untuk tidak ikut proses ini dengan menyetel opsi focusReset dari intercept() ke "manual":

navigateEvent.intercept({
  focusReset: 'manual',
  async handler() {
    // …
  },
});

Peristiwa keberhasilan dan kegagalan

Saat pengendali intercept() Anda dipanggil, salah satu dari dua hal berikut akan terjadi:

  • Jika Promise yang ditampilkan terpenuhi (atau Anda tidak memanggil intercept()), Navigation API akan memicu "navigatesuccess" dengan Event.
  • Jika Promise yang ditampilkan ditolak, API akan memicu "navigateerror" dengan ErrorEvent.

Peristiwa ini memungkinkan kode Anda menangani keberhasilan atau kegagalan secara terpusat. Misalnya, Anda dapat menangani keberhasilan dengan menyembunyikan indikator progres yang sebelumnya ditampilkan, seperti ini:

navigation.addEventListener('navigatesuccess', event => {
  loadingIndicator.hidden = true;
});

Atau Anda dapat menampilkan pesan error saat terjadi kegagalan:

navigation.addEventListener('navigateerror', event => {
  loadingIndicator.hidden = true; // also hide indicator
  showMessage(`Failed to load page: ${event.message}`);
});

Pemroses peristiwa "navigateerror", yang menerima ErrorEvent, sangat berguna karena dijamin akan menerima error apa pun dari kode Anda yang menyiapkan halaman baru. Anda cukup await fetch() mengetahui bahwa jika jaringan tidak tersedia, error pada akhirnya akan dirutekan ke "navigateerror".

navigation.currentEntry menyediakan akses ke entri saat ini. Ini adalah objek yang menjelaskan lokasi pengguna saat ini. Entri ini mencakup URL saat ini, metadata yang dapat digunakan untuk mengidentifikasi entri ini dari waktu ke waktu, dan status yang disediakan developer.

Metadata mencakup key, properti string unik dari setiap entri yang merepresentasikan entri saat ini dan slot-nya. Kunci ini tetap sama meskipun URL atau status entri saat ini berubah. Masih berada di slot yang sama. Sebaliknya, jika pengguna menekan Kembali, lalu membuka kembali halaman yang sama, key akan berubah karena entri baru ini membuat slot baru.

Bagi developer, key berguna karena Navigation API memungkinkan Anda mengarahkan pengguna secara langsung ke entri dengan kunci yang cocok. Anda dapat menahannya, bahkan di status entri lain, untuk berpindah antarhalaman dengan mudah.

// On JS startup, get the key of the first loaded page
// so the user can always go back there.
const {key} = navigation.currentEntry;
backToHomeButton.onclick = () => navigation.traverseTo(key);

// Navigate away, but the button will always work.
await navigation.navigate('/another_url').finished;

Negara Bagian

Navigation API menampilkan konsep "status", yaitu informasi yang disediakan developer yang disimpan secara persisten pada entri histori saat ini, tetapi tidak terlihat langsung oleh pengguna. API ini sangat mirip dengan, tetapi ditingkatkan dari, history.state di History API.

Di Navigation API, Anda dapat memanggil metode .getState() dari entri saat ini (atau entri apa pun) untuk menampilkan salinan statusnya:

console.log(navigation.currentEntry.getState());

Secara default, nilainya adalah undefined.

Status setelan

Meskipun objek status dapat diubah, perubahan tersebut tidak disimpan kembali dengan entri histori, jadi:

const state = navigation.currentEntry.getState();
console.log(state.count); // 1
state.count++;
console.log(state.count); // 2
// But:
console.info(navigation.currentEntry.getState().count); // will still be 1

Cara yang benar untuk menyetel status adalah selama navigasi skrip:

navigation.navigate(url, {state: newState});
// Or:
navigation.reload({state: newState});

Dengan newState dapat berupa objek yang dapat di-clone.

Jika Anda ingin memperbarui status entri saat ini, sebaiknya lakukan navigasi yang menggantikan entri saat ini:

navigation.navigate(location.href, {state: newState, history: 'replace'});

Kemudian, pemroses peristiwa "navigate" Anda dapat mengambil perubahan ini melalui navigateEvent.destination:

navigation.addEventListener('navigate', navigateEvent => {
  console.log(navigateEvent.destination.getState());
});

Memperbarui status secara serentak

Umumnya, lebih baik memperbarui status secara asinkron melalui navigation.reload({state: newState}), lalu pemroses "navigate" Anda dapat menerapkan status tersebut. Namun, terkadang perubahan status telah sepenuhnya diterapkan pada saat kode Anda mengetahuinya, seperti saat pengguna mengalihkan elemen <details>, atau pengguna mengubah status input formulir. Dalam kasus ini, Anda mungkin ingin memperbarui status agar perubahan ini dipertahankan melalui pemuatan ulang dan traversal. Hal ini dapat dilakukan menggunakan updateCurrentEntry():

navigation.updateCurrentEntry({state: newState});

Ada juga acara untuk mengetahui perubahan ini:

navigation.addEventListener('currententrychange', () => {
  console.log(navigation.currentEntry.getState());
});

Namun, jika Anda bereaksi terhadap perubahan status di "currententrychange", Anda mungkin memisahkan atau bahkan menduplikasi kode penanganan status antara peristiwa "navigate" dan peristiwa "currententrychange", sedangkan navigation.reload({state: newState}) akan memungkinkan Anda menanganinya di satu tempat.

Status vs. parameter URL

Karena status dapat berupa objek terstruktur, Anda mungkin tergoda untuk menggunakannya untuk semua status aplikasi. Namun, dalam banyak kasus, lebih baik menyimpan status tersebut di URL.

Jika Anda ingin status dipertahankan saat pengguna membagikan URL kepada pengguna lain, simpan status di URL. Jika tidak, objek status adalah opsi yang lebih baik.

Mengakses semua entri

Namun, "entri saat ini" bukan satu-satunya. API ini juga menyediakan cara untuk mengakses seluruh daftar entri yang telah dijelajahi pengguna saat menggunakan situs Anda melalui panggilan navigation.entries(), yang menampilkan array snapshot entri. Hal ini dapat digunakan untuk, misalnya, menampilkan UI yang berbeda berdasarkan cara pengguna membuka halaman tertentu, atau hanya untuk melihat kembali URL sebelumnya atau statusnya. Hal ini tidak mungkin dilakukan dengan History API saat ini.

Anda juga dapat memproses peristiwa "dispose" pada setiap NavigationHistoryEntry, yang dipicu saat entri tidak lagi menjadi bagian dari histori browser. Hal ini dapat terjadi sebagai bagian dari pembersihan umum, tetapi juga terjadi saat bernavigasi. Misalnya, jika Anda kembali ke 10 tempat, lalu maju, 10 entri histori tersebut akan dihapus.

Contoh

Peristiwa "navigate" diaktifkan untuk semua jenis navigasi, seperti yang disebutkan di atas. (Sebenarnya ada lampiran panjang dalam spesifikasi tentang semua kemungkinan jenis.)

Meskipun untuk banyak situs, kasus yang paling umum adalah saat pengguna mengklik <a href="...">, ada dua jenis navigasi yang lebih kompleks dan penting untuk dibahas.

Navigasi terprogram

Yang pertama adalah navigasi terprogram, di mana navigasi disebabkan oleh panggilan metode di dalam kode sisi klien Anda.

Anda dapat memanggil navigation.navigate('/another_page') dari mana saja dalam kode untuk menyebabkan navigasi. Hal ini akan ditangani oleh pemroses peristiwa terpusat yang terdaftar di pemroses "navigate", dan pemroses terpusat Anda akan dipanggil secara serentak.

Tujuannya adalah untuk meningkatkan penggabungan metode lama seperti location.assign() dan sejenisnya, serta metode pushState() dan replaceState() History API.

Metode navigation.navigate() menampilkan objek yang berisi dua instance Promise di { committed, finished }. Hal ini memungkinkan pemanggil menunggu hingga transisi "dilakukan" (URL yang terlihat telah berubah dan NavigationHistoryEntry baru tersedia) atau "selesai" (semua promise yang ditampilkan oleh intercept({ handler }) telah selesai—atau ditolak, karena gagal atau didahului oleh navigasi lain).

Metode navigate juga memiliki objek opsi, tempat Anda dapat menyetel:

  • state: status untuk entri histori baru, sebagaimana tersedia melalui metode .getState() di NavigationHistoryEntry.
  • history: yang dapat disetel ke "replace" untuk menggantikan entri histori saat ini.
  • info: objek yang akan diteruskan ke peristiwa navigasi melalui navigateEvent.info.

Khususnya, info dapat berguna untuk, misalnya, menunjukkan animasi tertentu yang menyebabkan halaman berikutnya muncul. (Alternatifnya adalah menetapkan variabel global atau menyertakannya sebagai bagian dari #hash. Kedua opsi tersebut agak canggung.) Khususnya, info ini tidak akan diputar ulang jika pengguna kemudian menyebabkan navigasi, misalnya, melalui tombol Kembali dan Maju. Faktanya, dalam kasus tersebut, nilainya akan selalu undefined.

Demo pembukaan dari kiri atau kanan

navigation juga memiliki sejumlah metode navigasi lainnya, yang semuanya menampilkan objek yang berisi { committed, finished }. Saya telah menyebutkan traverseTo() (yang menerima key yang menunjukkan entri tertentu dalam histori pengguna) dan navigate(). Ini juga mencakup back(), forward(), dan reload(). Semua metode ini ditangani—seperti navigate()—oleh pemroses peristiwa "navigate" terpusat.

Pengiriman Formulir

Kedua, pengiriman <form> HTML melalui POST adalah jenis navigasi khusus, dan Navigation API dapat mencegatnya. Meskipun menyertakan payload tambahan, navigasi tetap ditangani secara terpusat oleh pemroses "navigate".

Pengiriman formulir dapat dideteksi dengan mencari properti formData di NavigateEvent. Berikut adalah contoh yang mengubah pengiriman formulir apa pun menjadi pengiriman yang tetap berada di halaman saat ini melalui fetch():

navigation.addEventListener('navigate', navigateEvent => {
  if (navigateEvent.formData && navigateEvent.canIntercept) {
    // User submitted a POST form to a same-domain URL
    // (If canIntercept is false, the event is just informative:
    // you can't intercept this request, although you could
    // likely still call .preventDefault() to stop it completely).

    navigateEvent.intercept({
      // Since we don't update the DOM in this navigation,
      // don't allow focus or scrolling to reset:
      focusReset: 'manual',
      scroll: 'manual',
      handler() {
        await fetch(navigateEvent.destination.url, {
          method: 'POST',
          body: navigateEvent.formData,
        });
        // You could navigate again with {history: 'replace'} to change the URL here,
        // which might indicate "done"
      },
    });
  }
});

Apa yang kurang?

Meskipun pemroses peristiwa "navigate" bersifat terpusat, spesifikasi Navigation API saat ini tidak memicu "navigate" saat halaman dimuat pertama kali. Untuk situs yang menggunakan Server Side Rendering (SSR) untuk semua status, hal ini mungkin tidak masalah—server Anda dapat menampilkan status awal yang benar, yang merupakan cara tercepat untuk menampilkan konten kepada pengguna Anda. Namun, situs yang memanfaatkan kode sisi klien untuk membuat halamannya mungkin perlu membuat fungsi tambahan untuk menginisialisasi halamannya.

Pilihan desain Navigation API lainnya yang disengaja adalah bahwa API ini hanya beroperasi dalam satu frame—yaitu, halaman tingkat teratas, atau satu <iframe> tertentu. Hal ini memiliki sejumlah implikasi menarik yang didokumentasikan lebih lanjut dalam spesifikasi, tetapi dalam praktiknya, akan mengurangi kebingungan developer. History API sebelumnya memiliki sejumlah kasus ekstrem yang membingungkan, seperti dukungan untuk frame, dan Navigation API yang didesain ulang menangani kasus ekstrem ini sejak awal.

Terakhir, belum ada konsensus tentang mengubah atau menyusun ulang daftar entri yang telah dijelajahi pengguna secara terprogram. Hal ini sedang dibahas, tetapi salah satu opsinya adalah hanya mengizinkan penghapusan: baik entri historis maupun "semua entri mendatang". Yang terakhir akan memungkinkan status sementara. Misalnya, sebagai developer, saya dapat:

  • mengajukan pertanyaan kepada pengguna dengan membuka URL atau status baru
  • mengizinkan pengguna menyelesaikan pekerjaannya (atau Kembali)
  • menghapus entri histori setelah penyelesaian tugas

Hal ini bisa sangat cocok untuk modal atau interstisial sementara: URL baru adalah sesuatu yang dapat digunakan pengguna dengan gestur Kembali untuk keluar, tetapi mereka tidak dapat secara tidak sengaja menggunakan gestur Maju untuk membukanya lagi (karena entri telah dihapus). Hal ini tidak mungkin dilakukan dengan History API saat ini.

Mencoba Navigation API

Navigation API tersedia di Chrome 102 tanpa tanda. Anda juga dapat mencoba demo oleh Domenic Denicola.

Meskipun terlihat mudah, History API klasik tidak didefinisikan dengan baik dan memiliki sejumlah besar masalah terkait kasus sudut dan cara penerapannya yang berbeda di berbagai browser. Kami harap Anda mempertimbangkan untuk memberikan masukan tentang Navigation API baru.

Referensi

Ucapan terima kasih

Terima kasih kepada Thomas Steiner, Domenic Denicola, dan Nate Chapin yang telah meninjau postingan ini.