Pemilihan rute sisi klien modern: Navigation API

Menstandarkan perutean sisi klien melalui API baru yang sepenuhnya merombak pembangunan aplikasi web satu halaman.

Dukungan Browser

  • 102
  • 102
  • x
  • x

Sumber

Aplikasi web satu halaman, atau SPA, ditentukan oleh fitur inti: menulis ulang konten secara dinamis saat pengguna berinteraksi dengan situs, dan bukan metode default pemuatan halaman yang sama sekali baru dari server.

Meskipun SPA dapat menghadirkan fitur ini kepada Anda melalui History API (atau dalam kasus tertentu, dengan menyesuaikan bagian #hash situs), ini adalah API kaku yang dikembangkan jauh sebelum SPA menjadi norma—dan web meminta pendekatan yang benar-benar baru. Navigation API adalah API yang diusulkan yang benar-benar merombak ruang ini, bukan mencoba memperbaiki bagian-bagian kecil yang keseluruhan di History API. (Misalnya, Scroll Restoration mem-patch History API, bukan mencoba menciptakannya kembali.)

Postingan ini menjelaskan Navigation API di level tinggi. Jika Anda ingin membaca proposal teknis, lihat Draf Laporan di repositori WICG.

Contoh Penggunaan

Untuk menggunakan Navigation API, mulai 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 bolak-balik) atau saat navigasi dipicu secara terprogram (yaitu, melalui kode situs Anda). Dalam kebanyakan kasus, tindakan ini memungkinkan kode Anda mengganti perilaku default browser untuk tindakan tersebut. Untuk SPA, itu mungkin berarti menjaga pengguna tetap berada 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 pada situs. Tindakan ini akan membuat objek transisi, navigation.transition, yang dapat digunakan kode lain untuk melacak progres navigasi.

intercept() dan preventDefault() biasanya diizinkan, tetapi terkadang tidak dapat dipanggil. Anda tidak dapat menangani navigasi melalui intercept() jika navigasinya merupakan navigasi lintas asal. Anda tidak dapat membatalkan navigasi melalui preventDefault() jika pengguna menekan tombol Kembali atau Teruskan di browser; Anda tidak akan dapat menjebak pengguna di situs Anda. (Hal ini sedang dibahas di GitHub.)

Meskipun Anda tidak dapat menghentikan atau mengintersepsi navigasi itu sendiri, peristiwa "navigate" akan tetap diaktifkan. Data ini informatif, jadi kode Anda dapat, misalnya, mencatat peristiwa Analytics untuk menunjukkan bahwa pengguna meninggalkan situs Anda.

Mengapa menambahkan peristiwa lain ke platform?

Pemroses peristiwa "navigate" memusatkan penanganan perubahan URL di dalam SPA. Ini adalah proposisi yang sulit menggunakan API lama. Jika Anda pernah menulis perutean untuk SPA 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));

Boleh juga, 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 bisa disederhanakan—sesuatu yang dicapai Navigation API baru.

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

Secara pribadi, History API sering terasa bisa membantu berbagai kemungkinan ini. Namun, hal ini sebenarnya hanya memiliki dua area permukaan: merespons jika pengguna menekan Back atau Forward di browser, serta mengirim dan mengganti URL. Ini tidak memiliki analogi dengan "navigate", kecuali jika Anda menyiapkan pemroses secara manual 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, Anda tidak dapat menangkap navigasi. Navigasi lintas asal dan traversal lintas dokumen tidak dapat ditangkap.
destination.url
Mungkin merupakan 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. Dalam SPA modern, hash harus digunakan untuk menautkan ke berbagai bagian dokumen saat ini. Jadi, jika hashChange benar, Anda mungkin tidak perlu mencegat navigasi ini.
downloadRequest
Jika ini benar, navigasi dimulai oleh link dengan atribut download. Dalam sebagian besar kasus, Anda tidak perlu mencegatnya.
formData
Jika tidak null, berarti navigasi ini adalah bagian dari pengiriman formulir POST. Pastikan Anda mempertimbangkan hal ini saat menangani navigasi. Jika Anda hanya ingin menangani navigasi GET, hindari intersepsi navigasi yang formData bukan null. Lihat contoh penanganan pengiriman formulir nanti dalam artikel.
navigationType
Ini adalah salah satu dari "reload", "push", "replace", atau "traverse". Jika berstatus "traverse", navigasi ini tidak dapat dibatalkan melalui preventDefault().

Misalnya, fungsi shouldNotIntercept yang digunakan pada contoh pertama bisa 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
  );
}

Mencegat

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

Browser memulai dengan mengambil posisi scroll untuk status saat ini, sehingga dapat dipulihkan secara opsional nanti, lalu memanggil callback handler Anda. Jika handler menampilkan promise (yang terjadi secara otomatis dengan async functions), promise tersebut akan memberi tahu browser berapa lama waktu yang dibutuhkan untuk navigasi, dan apakah promise berhasil.

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 saat ini terjadi, seiring waktu, mengubah dokumen dari URL dan status sebelumnya ke URL dan status baru. Ini memiliki sejumlah potensi manfaat, termasuk aksesibilitas: browser dapat menampilkan awal, akhir, atau potensi kegagalan navigasi. Chrome, misalnya, mengaktifkan indikator pemuatan bawaannya, dan memungkinkan pengguna berinteraksi dengan tombol berhenti. (Saat ini hal ini tidak terjadi ketika pengguna menavigasi melalui tombol kembali/maju, tetapi hal ini akan segera diperbaiki.)

Saat mengintersepsi navigasi, URL baru akan berlaku tepat sebelum callback handler Anda dipanggil. Jika Anda tidak segera memperbarui DOM, tindakan ini akan membuat periode tempat 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 dibahas di GitHub, tetapi biasanya sebaiknya segera perbarui halaman dengan semacam 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);
      },
    });
  }
});

Cara ini tidak hanya menghindari masalah resolusi URL, tetapi juga terasa cepat karena Anda merespons pengguna secara langsung.

Batalkan sinyal

Karena Anda dapat melakukan pekerjaan asinkron di pengendali intercept(), navigasi mungkin akan menjadi redundan. Hal ini terjadi saat:

  • Pengguna mengklik link lain, atau beberapa kode melakukan navigasi lain. Dalam hal ini, navigasi lama ditinggalkan dan digantikan oleh navigasi baru.
  • Pengguna mengklik tombol 'stop' di browser.

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

Versi singkatnya adalah pada dasarnya menyediakan objek yang mengaktifkan peristiwa saat Anda harus menghentikan pekerjaan. Secara khusus, Anda dapat meneruskan AbortSignal ke panggilan apa pun yang Anda lakukan ke fetch(), yang akan membatalkan permintaan jaringan yang sedang berlangsung jika navigasi di-preempt. Tindakan ini akan menghemat bandwidth pengguna, dan menolak Promise yang ditampilkan oleh fetch(), sehingga mencegah kode berikut dari tindakan seperti memperbarui DOM agar menampilkan navigasi halaman yang sekarang tidak valid.

Berikut adalah contoh sebelumnya, tetapi dengan getArticleContent inline, menunjukkan cara AbortSignal dapat digunakan 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 melakukan intercept() navigasi, browser akan mencoba menangani scroll secara otomatis.

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

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

Secara default, hal ini terjadi setelah promise yang ditampilkan oleh handler selesai, tetapi jika akan di-scroll sebelumnya, 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 menetapkan 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 atribut autofocus yang ditetapkan, atau elemen <body> jika tidak ada elemen yang memiliki atribut tersebut.

Anda dapat memilih tidak ikut perilaku ini dengan menetapkan opsi focusReset dari intercept() ke "manual":

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

Peristiwa berhasil 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 mengaktifkan "navigatesuccess" dengan Event.
  • Jika Promise yang ditampilkan menolak, API akan mengaktifkan "navigateerror" dengan ErrorEvent.

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

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

Atau, Anda mungkin menampilkan pesan error jika gagal:

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 dari kode Anda yang menyiapkan halaman baru. Anda dapat await fetch() dengan mudah mengetahui bahwa jika jaringan tidak tersedia, error pada akhirnya akan dirutekan ke "navigateerror".

navigation.currentEntry memberikan 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 ini mencakup key, properti string unik dari setiap entri yang mewakili entri saat ini dan slotnya. Kunci ini tetap sama meskipun URL atau status entri saat ini berubah. Masih di slot yang sama. Sebaliknya, jika pengguna menekan Kembali lalu membuka kembali halaman yang sama, key akan berubah saat entri baru ini membuat slot baru.

Bagi developer, key berguna karena Navigation API memungkinkan Anda langsung mengarahkan pengguna ke entri dengan kunci yang cocok. Anda dapat mempertahankannya, bahkan dalam 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 gagasan "status", yaitu informasi yang disediakan developer yang disimpan secara persisten pada entri histori saat ini, tetapi tidak terlihat langsung oleh pengguna. Hal 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 menetapkan status adalah selama navigasi skrip:

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

Dengan newState dapat berupa objek clonable apa pun.

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 sinkron

Umumnya, sebaiknya perbarui status secara asinkron melalui navigation.reload({state: newState}), maka pemroses "navigate" dapat menerapkan status tersebut. Namun, terkadang perubahan status sudah diterapkan sepenuhnya pada saat kode Anda mendengarnya, 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 dimungkinkan menggunakan updateCurrentEntry():

navigation.updateCurrentEntry({state: newState});

Ada juga peristiwa untuk mendengar tentang perubahan ini:

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

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

Parameter Status vs. 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 tersebut di URL. Jika tidak, objek status adalah opsi yang lebih baik.

Mengakses semua entri

Namun, "entri saat ini" tidak semuanya. API ini juga menyediakan cara untuk mengakses seluruh daftar entri yang telah dibuka 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 diaktifkan 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 berjalan mundur 10 tempat, lalu menavigasi maju, 10 entri histori tersebut akan dibuang.

Contoh

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

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

Navigasi terprogram

Pertama adalah navigasi terprogram, dengan navigasi disebabkan oleh panggilan metode di dalam kode sisi klien Anda.

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

Hal ini dimaksudkan sebagai peningkatan agregasi metode lama seperti location.assign() dan teman, serta metode pushState() dan replaceState() History API.

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

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

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

Secara khusus, info dapat berguna, misalnya, untuk menunjukkan animasi tertentu yang menyebabkan halaman berikutnya muncul. (Alternatifnya adalah menetapkan variabel global atau menyertakannya sebagai bagian dari #hash. Kedua opsi tersebut agak aneh.) Secara khusus, info ini tidak akan di-replay jika nanti pengguna menyebabkan navigasi, misalnya melalui tombol Kembali dan Maju. Bahkan, nilainya akan selalu undefined dalam kasus tersebut.

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(). Daftar 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 masih ditangani secara terpusat oleh pemroses "navigate".

Pengiriman formulir dapat dideteksi dengan mencari properti formData di NavigateEvent. Berikut adalah contoh yang mengubah pengiriman formulir menjadi formulir 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" pada pemuatan pertama halaman. Selain itu, untuk situs yang menggunakan Rendering Sisi Server (SSR) untuk semua status, hal ini tidak masalah. Server Anda dapat menampilkan status awal yang benar, yang merupakan cara tercepat untuk mengirimkan konten kepada pengguna. Tetapi situs yang memanfaatkan kode sisi klien untuk membuat halaman mereka mungkin perlu membuat fungsi tambahan untuk melakukan inisialisasi halaman mereka.

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

Terakhir, belum ada konsensus tentang modifikasi atau pengaturan ulang secara terprogram daftar entri yang telah dinavigasi pengguna. Hal ini saat ini sedang dibahas, tetapi salah satu opsinya adalah hanya mengizinkan penghapusan: entri historis atau "semua entri mendatang". Yang kedua akan memungkinkan status sementara. Misalnya, sebagai developer, saya dapat:

  • ajukan pertanyaan kepada pengguna dengan membuka URL atau status baru
  • izinkan pengguna menyelesaikan pekerjaan mereka (atau kembali)
  • menghapus entri histori setelah tugas selesai

Ini bisa jadi sempurna untuk modal atau interstisial sementara: URL baru bisa dipakai pengguna dengan gestur Kembali untuk keluar, tetapi pengguna tidak bisa secara tidak sengaja memilih "Forward" untuk membukanya lagi (karena entri telah dihapus). Hal ini tidak dapat dilakukan dengan History API saat ini.

Mencoba Navigation API

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

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

Referensi

Ucapan terima kasih

Terima kasih kepada Thomas Steiner, Domenic Denicola, dan Nate Chapin telah meninjau postingan ini. Banner besar dari Unsplash, oleh Jeremy Zero.