Pemilihan rute sisi klien modern: Navigation API

Menstandarkan pemilihan rute sisi klien melalui API baru yang sepenuhnya merombak pembuatan aplikasi web satu halaman.

Dukungan Browser

  • 102
  • 102
  • x
  • x

Sumber

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

Meskipun SPA dapat menghadirkan fitur ini melalui History API (atau dalam kasus terbatas, dengan menyesuaikan bagian #hash situs), ini adalah API kaku yang dikembangkan jauh sebelum SPA menjadi standar—dan web memerlukan pendekatan yang sama sekali baru. Navigation API adalah API yang diusulkan yang benar-benar merombak ruang ini, bukan sekadar mem-patch detail History API. (Misalnya, Pemulihan Scroll menerapkan patch pada History API, bukan mencoba menciptakannya kembali.)

Postingan ini menjelaskan Navigation API secara umum. Jika Anda ingin 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 akan diaktifkan untuk semua jenis navigasi, baik 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, hal ini memungkinkan kode Anda mengganti perilaku default browser untuk tindakan itu. Untuk SPA, kemungkinan itu 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 ada kasus saat keduanya tidak dapat dipanggil. Anda tidak dapat menangani navigasi melalui intercept() jika navigasi tersebut adalah navigasi lintas asal. Dan Anda tidak dapat membatalkan navigasi melalui preventDefault() jika pengguna menekan tombol Kembali atau Teruskan di browser mereka; Anda tidak dapat menjebak pengguna di situs Anda. (Hal ini sedang dibahas di GitHub.)

Meskipun Anda tidak dapat menghentikan atau menghentikan navigasi itu sendiri, peristiwa "navigate" akan tetap diaktifkan. Informasi ini informatif, sehingga kode Anda dapat, misalnya, mencatat peristiwa Analytics ke dalam log untuk menunjukkan bahwa pengguna meninggalkan situs Anda.

Apa perlunya menambahkan acara lain ke platform?

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

Ini tidak masalah, tetapi tidak lengkap. Link mungkin masuk dan keluar di halaman Anda, dan ini 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 oleh Navigation API baru.

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

Secara pribadi, History API sering terasa dapat membantu meningkatkan kemungkinannya. Namun, metode ini hanya memiliki dua area platform: merespons jika pengguna menekan tombol Kembali atau Teruskan di browser, serta mendorong dan mengganti URL. Kode 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 menentukan cara menangani navigasi tertentu.

Properti utamanya adalah:

canIntercept
Jika ini salah, Anda tidak dapat menangkap navigasi. Navigasi lintas asal dan traversal lintas dokumen tidak dapat dicegat.
destination.url
Mungkin informasi terpenting yang perlu dipertimbangkan saat menangani navigasi.
hashChange
Benar jika navigasi adalah dokumen yang sama, dan hash adalah satu-satunya bagian dari URL yang berbeda dengan URL saat ini. Dalam SPA modern, hash digunakan untuk menautkan ke berbagai bagian dokumen saat ini. Jadi, jika hashChange bernilai benar, Anda mungkin tidak perlu menghentikan navigasi ini.
downloadRequest
Jika ini benar, navigasi dimulai oleh link dengan atribut download. Dalam sebagian besar kasus, Anda tidak perlu mencegatnya.
formData
Jika bukan 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 jika formData bukan null. Lihat contoh tentang penanganan pengiriman formulir nanti dalam artikel.
navigationType
Ini adalah salah satu dari "reload", "push", "replace", atau "traverse". Jika "traverse", navigasi ini tidak dapat dibatalkan melalui preventDefault().

Misalnya, fungsi shouldNotIntercept yang digunakan dalam contoh pertama mungkin 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 memanggil intercept({ handler }) dari dalam pemroses "navigate"-nya, kode Anda akan memberi tahu browser bahwa browser sedang menyiapkan halaman untuk status baru yang diperbarui, dan bahwa navigasi mungkin memerlukan waktu beberapa saat.

Browser memulai dengan mengambil posisi scroll untuk status saat ini, sehingga dapat dipulihkan nanti secara opsional, lalu memanggil callback handler Anda. Jika handler menampilkan promise (yang terjadi secara otomatis dengan async functions), promise tersebut akan memberi tahu browser berapa lama navigasi yang dibutuhkan, dan apakah navigasi tersebut 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);
      },
    });
  }
});

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

Saat mengintersep navigasi, URL baru akan berlaku tepat sebelum callback handler Anda dipanggil. Jika Anda tidak segera memperbarui DOM, tindakan ini akan menghasilkan periode penayangan konten lama 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 umumnya disarankan untuk segera memperbarui 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);
      },
    });
  }
});

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

Batalkan sinyal

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

  • Pengguna mengklik link lain, atau beberapa kode menjalankan navigasi lain. Dalam hal ini, navigasi lama akan ditinggalkan dan digantikan dengan 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 pendeknya pada dasarnya menyediakan objek yang memicu peristiwa ketika Anda harus menghentikan pekerjaan. Secara khusus, Anda dapat meneruskan AbortSignal ke semua panggilan yang Anda lakukan ke fetch(), yang akan membatalkan permintaan jaringan yang sedang berlangsung jika navigasi dihentikan. Tindakan ini akan menghemat bandwidth pengguna dan menolak Promise yang ditampilkan oleh fetch(), sehingga mencegah kode berikut melakukan tindakan seperti memperbarui DOM agar menampilkan navigasi halaman yang sekarang tidak valid.

Berikut adalah contoh sebelumnya, tetapi dengan getArticleContent yang disisipkan, 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() pada navigasi, browser akan mencoba menangani scroll 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 (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 di-resolve, tetapi jika Anda 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 menangani 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 di-resolve, browser akan memfokuskan elemen pertama dengan atribut autofocus yang disetel, 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 gagal

Saat pengendali intercept() Anda dipanggil, salah satu dari 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 progres yang sebelumnya ditampilkan, 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 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 mendeskripsikan posisi 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 menyertakan key, properti string unik dari setiap entri yang mewakili entri saat ini dan slot-nya. Kunci ini tetap sama meskipun URL atau status entri saat ini berubah. Item tersebut masih berada di slot yang sama. Sebaliknya, jika pengguna menekan tombol 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 langsung mengarahkan pengguna ke entri dengan kunci yang cocok. Anda dapat mempertahankannya, bahkan dalam status entri lain, untuk berpindah antar laman 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 memunculkan gagasan "status", yaitu informasi yang disediakan developer yang disimpan secara persisten pada entri histori saat ini, tetapi tidak terlihat langsung oleh pengguna. 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 dimutasi, perubahan tersebut tidak disimpan kembali dengan entri histori, sehingga:

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 yang dapat dieksekusi.

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 menerapkan 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}), sehingga pemroses "navigate" dapat menerapkan status tersebut. Namun, terkadang perubahan status telah sepenuhnya diterapkan pada saat kode Anda mendengarnya, seperti saat pengguna mengalihkan elemen <details>, atau pengguna mengubah status input formulir. Dalam kasus ini, Anda mungkin ingin mengupdate status sehingga perubahan ini dipertahankan melalui pemuatan ulang dan traversal. Hal ini dapat dilakukan menggunakan updateCurrentEntry():

navigation.updateCurrentEntry({state: newState});

Ada juga acara yang perlu Anda dengar tentang 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 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 menjadi 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 berbagi URL dengan pengguna lain, simpan di URL. Jika tidak, objek status adalah opsi yang lebih baik.

Mengakses semua entri

Namun, "entri saat ini" bukan semuanya. API ini juga menyediakan cara untuk mengakses seluruh daftar entri yang dijelajahi pengguna saat menggunakan situs Anda melalui panggilan navigation.entries(), yang menampilkan array ringkasan 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" di 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 melintasi 10 tempat sebelumnya, lalu menavigasi ke depan, 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 memungkinkan.)

Meskipun bagi banyak situs, kasus yang paling umum terjadi adalah saat pengguna mengklik <a href="...">, ada dua jenis navigasi penting yang 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 melakukan 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 agregasi yang lebih baik dari metode lama seperti location.assign() dan teman, ditambah metode History API pushState() dan replaceState().

Metode navigation.navigate() menampilkan objek yang berisi dua instance Promise di { committed, finished }. Hal ini memungkinkan invoker dapat 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 kegagalan 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() pada NavigationHistoryEntry.
  • history: yang dapat ditetapkan ke "replace" untuk mengganti 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 mungkin dengan 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 Mundur dan Maju. Bahkan dalam kasus tersebut, nilainya akan selalu undefined.

Demo membuka dari kiri atau kanan

navigation juga memiliki sejumlah metode navigasi lain, semuanya menampilkan objek yang berisi { committed, finished }. Saya telah menyebutkan traverseTo() (yang menerima key yang menunjukkan entri tertentu dalam histori pengguna) dan navigate(). Properti 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 ini contoh yang hanya mengubah pengiriman formulir menjadi pengiriman 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 mungkin tidak menjadi masalah—server Anda dapat menampilkan status awal yang benar, yang merupakan cara tercepat untuk mengirimkan konten kepada pengguna. Namun, situs yang memanfaatkan kode sisi klien untuk membuat halaman mungkin perlu membuat fungsi tambahan untuk melakukan inisialisasi halaman.

Pilihan desain lainnya dari Navigation API adalah API tersebut 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 bingkai, dan Navigation API yang telah dikonsep ulang menangani kasus ekstrem ini sejak awal.

Terakhir, belum ada konsensus tentang cara memodifikasi atau mengatur ulang daftar entri yang telah dinavigasi oleh pengguna secara terprogram. Opsi ini sedang dibahas, tetapi salah satu opsinya adalah mengizinkan penghapusan saja: 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
  • memungkinkan pengguna untuk menyelesaikan pekerjaan mereka (atau kembali)
  • menghapus entri riwayat setelah menyelesaikan tugas

Ini bisa jadi sangat cocok untuk modal sementara atau interstisial: URL baru adalah sesuatu yang dapat ditinggalkan pengguna dengan menggunakan gestur Kembali, tetapi mereka kemudian tidak dapat secara tidak sengaja Meneruskan 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 tampak mudah, API ini tidak didefinisikan dengan baik dan memiliki sejumlah besar masalah seputar kasus sudut dan cara penerapannya secara berbeda di seluruh 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.