Modern istemci tarafı yönlendirme: Navigation API

Tek sayfalık uygulamaların oluşturulmasını tamamen yeni bir API ile standart hale getirerek istemci tarafı yönlendirmeyi standart hale getiriyor.

Tarayıcı desteği

  • Chrome: 102.
  • Edge: 102.
  • Firefox: Desteklenmez.
  • Safari: Desteklenmez.

Kaynak

Tek sayfalı uygulamalar (SPA'lar) temel bir özellikle tanımlanır: Varsayılan yöntem olan sunucudan tamamen yeni sayfalar yüklemek yerine, kullanıcı siteyle etkileşim kurarken içeriğini dinamik olarak yeniden yazar.

SPA'lar, History API aracılığıyla (veya sınırlı durumlarda sitenin #hash bölümünü ayarlayarak) bu özelliği sunabilse de History API, SPA'ların yaygınlaşmasından çok önce geliştirilmiş hantal bir API'dir ve web'de tamamen yeni bir yaklaşıma ihtiyaç duyulmaktadır. Navigation API, History API'nin eksikliklerini gidermek yerine bu alanı tamamen yenileyen önerilen bir API'dir. (Örneğin, Kaydırma Geri Yükleme, History API'yi yeniden icat etmek yerine bu API'ye bir yama uyguladı.)

Bu yayında, Gezinme API'si genel hatlarıyla açıklanmaktadır. Teknik öneriyi okumak için WICG deposundaki taslak raporu inceleyin.

Örnek kullanım

Navigation API'yi kullanmak için global navigation nesnesine bir "navigate" dinleyici ekleyerek başlayın. Bu etkinlik temel olarak merkezidir: Kullanıcı bir işlem gerçekleştirdiğinde (ör.bağlantı tıklama, form gönderme veya geri ve ileri gitme) ya da gezinme programlı olarak tetiklendiğinde (ör. sitenizin kodu aracılığıyla) tüm gezinme türleri için etkinleştirilir. Çoğu durumda, kodunuzun tarayıcının ilgili işlem için varsayılan davranışını geçersiz kılmasına olanak tanır. SPA'lar için bu, muhtemelen kullanıcıyı aynı sayfada tutmak ve sitenin içeriğini yüklemek veya değiştirmek anlamına gelir.

"navigate" dinleyicisine, hedef URL gibi gezinmeyle ilgili bilgileri içeren ve gezinmeye tek bir merkezi yerden yanıt vermenize olanak tanıyan bir NavigateEvent iletilir. Temel bir "navigate" dinleyicisi şu şekilde görünebilir:

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});
  }
});

Gezinmeyle ilgili işlemleri iki şekilde yapabilirsiniz:

  • Gezinmeyi yönetmek için intercept({ handler }) (yukarıda açıklandığı gibi) çağrılıyor.
  • preventDefault() çağrısı yaparak navigasyonu tamamen iptal edebilirsiniz.

Bu örnekte, etkinlikte intercept() çağrısı yapılmaktadır. Tarayıcı, sitenizin sonraki durumunu yapılandırması gereken handler geri çağırma işlevinizi çağırır. Bu işlem, diğer kodun gezinme işleminin ilerleme durumunu izlemek için kullanabileceği bir geçiş nesnesi (navigation.transition) oluşturur.

Genellikle hem intercept() hem de preventDefault()'e izin verilir ancak çağrılamayacakları durumlar da vardır. Gezinme kaynaktan farklı bir kaynaktaysa intercept() aracılığıyla gezinmeleri işleyemezsiniz. Ayrıca, kullanıcı tarayıcısında Geri veya İleri düğmelerine basıyorsa preventDefault() aracılığıyla gezinmeyi iptal edemezsiniz. Kullanıcılarınızı sitenizde tutamamanız gerekir. (Bu konu GitHub'da tartışılıyor.)

Navigasyonu durduramaz veya durduramazsanız bile "navigate" etkinliği tetiklenir. Bilgilendirici olduğundan kodunuz, örneğin bir kullanıcının sitenizden ayrıldığını belirtmek için bir Analytics etkinliği günlüğe kaydedebilir.

Platforma neden başka bir etkinlik eklemelisiniz?

"navigate" etkinlik işleyici, URL değişikliklerini SPA'da merkezileştirir. Bu, eski API'leri kullananlar için zor bir tekliftir. History API'yi kullanarak kendi SPA'nızın yönlendirmesini yazdıysanız aşağıdaki gibi bir kod eklemiş olabilirsiniz:

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));

Bu iyi bir başlangıç olsa da yeterli değildir. Sayfanızdaki bağlantılar zaman içinde değişebilir. Ayrıca, kullanıcıların sayfalar arasında gezinmesinin tek yolu bağlantılar değildir. Örneğin, bir form gönderebilir veya hatta resim haritası kullanabilirler. Sayfanız bu işlemleri gerçekleştirebilir ancak basitleştirilebilecek çok sayıda olasılık vardır. Yeni Navigation API, bu olasılıkları basitleştirir.

Ayrıca, yukarıdaki yöntem geri/ileri gezinmeyi desteklemez. Bunun için başka bir etkinlik var "popstate".

Kişisel olarak, History API'nin bu olasılıklara yardımcı olabileceğini düşünüyorum. Ancak aslında yalnızca iki yüzey alanı vardır: Kullanıcının tarayıcısında Geri veya İleri tuşuna basması durumunda yanıt verme ve URL'leri gönderme ve değiştirme. Yukarıda gösterildiği gibi, tıklama etkinlikleri için dinleyicileri manuel olarak ayarlamanız dışında "navigate" ile benzerliği yoktur.

Gezinme öğelerini nasıl ele alacağınıza karar verme

navigateEvent, belirli bir gezinmeyle nasıl başa çıkacağınıza karar vermek için kullanabileceğiniz gezinme hakkında birçok bilgi içerir.

Temel özellikler şunlardır:

canIntercept
Bu değer yanlışsa gezinme işlemine müdahale edemezsiniz. Kaynaklar arası gezinmeler ve dokümanlar arası geçişler engellenemez.
destination.url
Gezinmeyle ilgili dikkate almanız gereken en önemli bilgi muhtemelen budur.
hashChange
Gezinme aynı dokümandaysa ve karma oluşturma işlemi, URL'nin mevcut URL'den farklı olan tek parçasıysa doğru değerini döndürür. Modern SPA'larda karma oluşturma işlemi, geçerli belgenin farklı bölümlerine bağlantı oluşturmak için kullanılmalıdır. Dolayısıyla, hashChange doğruysa bu gezinme işlemini durdurmanıza muhtemelen gerek yoktur.
downloadRequest
Bu doğruysa gezinme, download özelliğine sahip bir bağlantı tarafından başlatılmıştır. Çoğu durumda bu işlemi durdurmanız gerekmez.
formData
Bu değer null değilse bu gezinme, POST form gönderme işleminin bir parçasıdır. Gezinme sırasında bunu göz önünde bulundurun. Yalnızca GET gezinmelerini işlemek istiyorsanız formData değerinin null olmadığı gezinmelerin yolunu kesmekten kaçının. Makalenin ilerleyen bölümlerinde form gönderimlerini işlemeyle ilgili örneği inceleyin.
navigationType
Bu, "reload", "push", "replace" veya "traverse" değerlerinden biridir. "traverse" ise bu gezinme preventDefault() üzerinden iptal edilemez.

Örneğin, ilk örnekte kullanılan shouldNotIntercept işlevi şöyle olabilir:

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
  );
}

Araya girme

Kodunuz "navigate" dinleyicisinden intercept({ handler })'ü çağrdığında, sayfayı yeni ve güncellenmiş duruma hazırladığını ve gezinmenin biraz zaman alabileceğini tarayıcıya bildirir.

Tarayıcı, mevcut durumun kaydırma konumunu yakalayarak başlar. Böylece, isteğe bağlı olarak daha sonra geri yüklenebilir. Ardından handler geri çağırma işlevinizi çağırır. handler işleviniz bir promise döndürüyorsa (asynchronize işlevlerde bu otomatik olarak gerçekleşir) bu promise tarayıcıya gezinmenin ne kadar sürdüğünü ve başarılı olup olmadığını bildirir.

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);
      },
    });
  }
});

Bu nedenle bu API, tarayıcının anladığı semantik bir kavram sunar: Şu anda bir SPA gezinmesi gerçekleşmektedir. Bu gezinme, zaman içinde dokümanı önceki URL'den ve durumdan yeni bir URL'ye ve duruma değiştirir. Bu, erişilebilirlik de dahil olmak üzere çeşitli potansiyel avantajlara sahiptir: Tarayıcılar, gezinmenin başlangıcını, sonunu veya olası hatasını gösterebilir. Örneğin Chrome, yerel yükleme göstergesini etkinleştirir ve kullanıcının durdur düğmesiyle etkileşime geçmesine olanak tanır. (Bu durum, kullanıcı geri/ileri düğmelerini kullanarak gezinirken şu anda gerçekleşmiyor ancak yakında düzeltilecek.)

Gezinme işlemlerine müdahale edildiğinde yeni URL, handler geri çağırma işleviniz çağrılmadan hemen önce geçerli olur. DOM'u hemen güncellemezseniz eski içeriğin yeni URL ile birlikte gösterildiği bir dönem oluşur. Bu durum, veri alırken veya yeni alt kaynaklar yüklerken göreli URL çözümü gibi işlemleri etkiler.

URL değişikliğini erteleme yöntemi GitHub'da tartışılmaktadır ancak genellikle sayfayı, gelen içerik için bir tür yer tutucu ile hemen güncellemeniz önerilir:

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);
      },
    });
  }
});

Bu, URL çözümleme sorunlarını önlemekle kalmaz, kullanıcıya anında yanıt verdiğiniz için hızlı bir deneyim de sunar.

İptal sinyalleri

intercept() işleyicisinde eşzamansız çalışma yapabildiğiniz için gezinmenin gereksiz hale gelmesi mümkündür. Bu durum aşağıdaki durumlarda ortaya çıkar:

  • Kullanıcı başka bir bağlantıyı tıklar veya bazı kodlar başka bir gezinme işlemi gerçekleştirir. Bu durumda, eski gezinme menüsü yeni gezinme menüsü lehine terk edilir.
  • Kullanıcı tarayıcıda "durdur" düğmesini tıklar.

Bu olasılıklardan herhangi biriyle başa çıkmak için "navigate" dinleyicisine iletilen etkinlik, AbortSignal olan bir signal mülkü içerir. Daha fazla bilgi için İptal edilebilir getirme bölümüne bakın.

Özetlemek gerekirse, temel olarak çalışmanızı durdurmanız gerektiğinde bir etkinlik tetikleyen bir nesne sağlar. Özellikle, fetch() adresine yaptığınız tüm aramalara bir AbortSignal iletebilirsiniz. Bu, gezinme öncelikli hale getirilirse uçuş sırasındaki ağ isteklerini iptal eder. Bu işlem hem kullanıcının bant genişliğini korur hem de fetch() tarafından döndürülen Promise öğesini reddeder. Böylece, sonraki kodların DOM'u geçersiz bir sayfa gezinmesi gösterecek şekilde güncellemesi gibi işlemleri engeller.

Aşağıda, önceki örnekte getArticleContent satır içine yerleştirilmiş şekilde gösterilmektedir. Bu örnekte, AbortSignal'un fetch() ile nasıl kullanılabileceği gösterilmektedir:

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);
      },
    });
  }
});

Kaydırma işleme

Bir navigasyonu intercept() ettiğinizde tarayıcı, kaydırma işlemini otomatik olarak gerçekleştirmeye çalışır.

Yeni bir geçmiş girişine yapılan gezinmelerde (navigationEvent.navigationType "push" veya "replace" olduğunda), bu, URL parçasıyla (#'ten sonraki kısım) belirtilen bölüme kaydırmaya çalışma veya kaydırma çubuğunu sayfanın en üstüne sıfırlama anlamına gelir.

Yeniden yükleme ve geçişlerde bu, kaydırma konumunun bu geçmiş girişinin son kez görüntülendiği yere geri yükleneceği anlamına gelir.

Bu işlem varsayılan olarak, handler tarafından döndürülen söz çözüldükten sonra gerçekleşir. Ancak daha önce kaydırmanın mantıklı olduğu durumlarda navigateEvent.scroll()'ı çağırabilirsiniz:

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);
      },
    });
  }
});

Alternatif olarak, intercept() scroll seçeneğini "manual" olarak ayarlayarak otomatik kaydırma işlevini tamamen devre dışı bırakabilirsiniz:

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

Odak işleme

handler tarafından döndürülen söz çözüldükten sonra tarayıcı, autofocus özelliği ayarlanmış ilk öğeyi veya bu özelliğe sahip öğe yoksa <body> öğesini odaklar.

intercept() için focusReset seçeneğini "manual" olarak ayarlayarak bu davranışı devre dışı bırakabilirsiniz:

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

Başarı ve başarısızlık etkinlikleri

intercept() işleyiciniz çağrıldığında aşağıdaki iki durumdan biri gerçekleşir:

  • Döndürülen Promise koşulu karşılıyorsa (veya intercept() çağrılmadıysa) Gezinme API'si Event ile "navigatesuccess"'yi tetikler.
  • Döndürülen Promise reddederse API, "navigateerror"ErrorEvent ile tetikler.

Bu etkinlikler, kodunuzun başarı veya başarısızlıkla merkezi bir şekilde ilgilenmesine olanak tanır. Örneğin, daha önce gösterilen bir ilerleme göstergesini gizleyerek başarıyı ele alabilirsiniz.

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

Başarısızlık durumunda da bir hata mesajı gösterilebilir:

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

ErrorEvent alan "navigateerror" etkinlik dinleyicisi, yeni bir sayfa oluşturan kodunuzdan hata alacağı garanti edildiğinden özellikle kullanışlıdır. Ağ kullanılamıyorsa hatanın "navigateerror" adresine yönlendirileceğini bilerek await fetch() yapabilirsiniz.

navigation.currentEntry, geçerli girişe erişim sağlar. Bu, kullanıcının şu anda nerede olduğunu açıklayan bir nesnedir. Bu giriş, geçerli URL'yi, bu girişi zaman içinde tanımlamak için kullanılabilecek meta verileri ve geliştirici tarafından sağlanan durumu içerir.

Meta veriler, her girişin mevcut girişi ve yuvasını temsil eden benzersiz bir dize özelliği olan key öğesini içerir. Mevcut girişin URL'si veya durumu değişse bile bu anahtar aynı kalır. Kart hâlâ aynı yuvada. Buna karşılık, kullanıcı Geri tuşuna basıp aynı sayfayı yeniden açarsa bu yeni giriş yeni bir yuva oluşturduğundan key değişir.

Gezinme API'si, kullanıcıyı doğrudan eşleşen bir anahtara sahip bir girişe yönlendirmenize olanak tanıdığından key geliştiriciler için kullanışlıdır. Sayfalar arasında kolayca atlamak için diğer girişlerin durumlarında bile bu düğmeyi basılı tutabilirsiniz.

// 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;

Eyalet

Navigation API, geliştirici tarafından sağlanan ve mevcut geçmiş girişinde kalıcı olarak depolanan ancak kullanıcı tarafından doğrudan görülemeyen "durum" bilgisini gösterir. Bu, History API'deki history.state işlevine çok benzer ancak ondan daha gelişmiştir.

Navigation API'de, geçerli girişin (veya herhangi bir girişin) .getState() yöntemini çağırarak durumunun bir kopyasını döndürebilirsiniz:

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

Varsayılan olarak bu değer undefined olur.

Ayar durumu

Durum nesneleri değiştirilebilir olsa da bu değişiklikler geçmiş girişiyle birlikte geri kaydedilmez. Bu nedenle:

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

Durumu ayarlamak için doğru yöntem, komut dosyasında gezinme sırasındadır:

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

Burada newState, herhangi bir kopyalanabilir nesne olabilir.

Mevcut girişin durumunu güncellemek istiyorsanız en iyi yöntem, mevcut girişin yerini alacak bir gezinme gerçekleştirmektir:

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

Ardından "navigate" etkinlik dinleyiciniz, navigateEvent.destination aracılığıyla bu değişikliği algılayabilir:

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

Durumu senkronize olarak güncelleme

Genellikle durumu navigation.reload({state: newState}) aracılığıyla asenkron olarak güncellemek daha iyidir. Ardından "navigate" dinleyiciniz bu durumu uygulayabilir. Ancak bazen durum değişikliği, kodunuz bu değişikliği öğrenene kadar zaten tamamen uygulanmış olur (ör. kullanıcı bir <details> öğesini etkinleştirip devre dışı bıraktığında veya kullanıcı bir form girişinin durumunu değiştirdiğinde). Bu durumlarda, bu değişikliklerin yeniden yükleme ve geçiş işlemleri sırasında korunması için durumu güncelleyebilirsiniz. Bu işlem updateCurrentEntry() kullanılarak yapılabilir:

navigation.updateCurrentEntry({state: newState});

Bu değişiklik hakkında bilgi edinmek için katılabileceğiniz bir etkinlik de düzenlenmektedir:

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

Ancak "currententrychange"'te durum değişikliklerine tepki verdiğinizi fark ederseniz durum işleme kodunuzu "navigate" etkinliği ile "currententrychange" etkinliği arasında bölüyor veya hatta kopyalıyorsunuz demektir. navigation.reload({state: newState}) ise bu işlemi tek bir yerde yapmanıza olanak tanır.

Eyalet ve URL parametreleri

Durum, yapılandırılmış bir nesne olabileceğinden tüm uygulama durumunuz için kullanmak cazip gelebilir. Ancak çoğu durumda bu durumu URL'de depolamak daha iyidir.

Kullanıcı URL'yi başka bir kullanıcıyla paylaştığında durumun korunmasını istiyorsanız durumu URL'de saklayın. Aksi takdirde, durum nesnesi daha iyi bir seçenektir.

Tüm girişlere erişme

Ancak "mevcut giriş" bununla sınırlı değildir. API, kullanıcının sitenizi kullanırken gezindiği girişlerin tamamına erişmenize de olanak tanır. Bunun için navigation.entries() çağrısı kullanılır. Bu çağrı, girişlerin anlık görüntü dizisini döndürür. Bu, örneğin, kullanıcının belirli bir sayfaya nasıl gittiğine bağlı olarak farklı bir kullanıcı arayüzü göstermek veya yalnızca önceki URL'lere ya da bunların durumlarına bakmak için kullanılabilir. Mevcut History API ile bu işlem mümkün değildir.

Ayrıca, giriş artık tarayıcı geçmişinin bir parçası olmadığında tetiklenen NavigationHistoryEntry'larda "dispose" etkinliğini de dinleyebilirsiniz. Bu durum, genel temizlik kapsamında olabileceği gibi gezinirken de ortaya çıkabilir. Örneğin, 10 yer geri gidip ileri giderseniz bu 10 geçmiş girişi silinir.

Örnekler

"navigate" etkinliği, yukarıda belirtildiği gibi tüm gezinme türleri için tetiklenir. (Aslında, mümkün olan tüm türlerin spesifikasyonda yer aldığı uzun bir ek vardır.)

Birçok sitede en yaygın durum kullanıcının bir <a href="..."> tıklamasıdır. Bununla birlikte, ele alınması gereken iki önemli ve daha karmaşık gezinme türü vardır.

Programatik gezinme

İlki, istemci tarafı kodunuzdaki bir yöntem çağrısının neden olduğu programatik gezinmedir.

Gezinme işlemi başlatmak için kodunuzun herhangi bir yerinden navigation.navigate('/another_page') işlevini çağırabilirsiniz. Bu işlem, "navigate" dinleyicisine kayıtlı merkezi etkinlik işleyici tarafından gerçekleştirilir ve merkezi dinleyiciniz senkronize olarak çağrılır.

Bu, location.assign() ve arkadaşlar gibi eski yöntemlerin yanı sıra History API'nin pushState() ve replaceState() yöntemlerinin geliştirilmiş bir toplama işlemi olarak tasarlanmıştır.

navigation.navigate() yöntemi, { committed, finished } içinde iki Promise örneği içeren bir nesne döndürür. Bu sayede, çağıran, geçişin "taahhüt edilene" (görünür URL değişti ve yeni bir NavigationHistoryEntry kullanılabilir) veya "tamamlanana" (intercept({ handler }) tarafından döndürülen tüm sözler tamamlandı veya başarısızlık ya da başka bir gezinme tarafından öncelik verilmesi nedeniyle reddedildi) kadar bekleyebilir.

navigate yönteminde, aşağıdakileri ayarlayabileceğiniz bir options nesnesi de vardır:

  • state: NavigationHistoryEntry üzerindeki .getState() yöntemi aracılığıyla kullanılabilen yeni geçmiş girişinin durumu.
  • history: Geçerli geçmiş girişini değiştirmek için "replace" olarak ayarlanabilir.
  • info: navigateEvent.info aracılığıyla gezinme etkinliğine iletilecek bir nesne.

Özellikle info, örneğin bir sonraki sayfanın görünmesine neden olan belirli bir animasyonu belirtmek için yararlı olabilir. (Alternatif olarak, global bir değişken ayarlayabilir veya #hash'in bir parçası olarak dahil edebilirsiniz. Her iki seçenek de biraz garip.) Kullanıcı daha sonra Geri ve İleri düğmelerini kullanarak gezinme yaparsa bu info yeniden oynatılmaz. Aslında bu durumlarda her zaman undefined olur.

Sol veya sağdan açma demo'su

navigation, { committed, finished } içeren bir nesne döndüren başka gezinme yöntemlerine de sahiptir. traverseTo() (kullanıcı geçmişindeki belirli bir girişi belirten bir key kabul eder) ve navigate() hakkında daha önce bahsetmiştik. back(), forward() ve reload() de bu kapsamdadır. Bu yöntemlerin tümü, navigate() gibi merkezi "navigate" etkinlik işleyici tarafından işlenir.

Form Gönderimleri

İkinci olarak, POST aracılığıyla HTML <form> gönderimi özel bir gezinme türüdür ve Navigation API bu işlemin önüne geçebilir. Ek bir yükü olsa da gezinme, "navigate" dinleyici tarafından merkezi olarak yönetilir.

Form gönderimi, NavigateEvent üzerinde formData mülkü aranarak algılanabilir. Aşağıda, herhangi bir form gönderimini fetch() aracılığıyla mevcut sayfada kalan bir gönderime dönüştüren bir örnek verilmiştir:

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"
      },
    });
  }
});

Eksikler

"navigate" etkinlik dinleyicisinin merkezi yapısına rağmen mevcut Navigation API spesifikasyonu, bir sayfanın ilk yüklenmesinde "navigate"'ü tetiklemez. Tüm durumlar için sunucu tarafı oluşturma (SSR) kullanan sitelerde bu sorun olmayabilir. Sunucunuz, kullanıcılarınıza içerik sunmanın en hızlı yolu olan doğru ilk durumu döndürebilir. Ancak sayfalarını oluşturmak için istemci tarafı kodundan yararlanan sitelerin, sayfalarını başlatmak için ek bir işlev oluşturması gerekebilir.

Gezinme API'sinin bilinçli bir tasarım tercihi de yalnızca tek bir çerçevede (yani en üst düzey sayfa veya tek bir belirli <iframe>) çalışmasıdır. Bu, özellikte daha ayrıntılı olarak açıklanan bir dizi ilginç sonucun yanı sıra pratikte geliştiricilerin kafa karışıklığını azaltacaktır. Önceki History API'de, çerçeve desteği gibi kafa karıştırıcı bazı uç durumlar vardır. Yeniden tasarlanan Navigation API ise bu uç durumları baştan ele alır.

Son olarak, kullanıcının gezindiği girişlerin listesini programatik olarak değiştirme veya yeniden düzenleme konusunda henüz bir fikir birliğine varılmadı. Bu konu şu anda tartışılıyor ancak yalnızca silme işlemlerine izin vermek (geçmiş girişler veya "gelecekteki tüm girişler") bir seçenek olabilir. İkincisi, geçici duruma izin verir. Örneğin, geliştirici olarak şunları yapabilirim:

  • Yeni URL'ye veya duruma giderek kullanıcıya soru sorabilirsiniz.
  • Kullanıcının işini tamamlamasına izin verin (veya Geri'ye gidin)
  • Görev tamamlandığında geçmiş girişini kaldırma

Bu, geçici modallar veya geçiş reklamları için mükemmel olabilir: Yeni URL, kullanıcının Geri hareketini kullanarak ayrılabileceği bir URL'dir ancak kullanıcı, giriş kaldırıldığı için yanlışlıkla İleri'ye giderek URL'yi tekrar açamaz. Bu, mevcut History API ile mümkün değildir.

Navigation API'yi deneyin

Navigation API, Chrome 102'de flag olmadan kullanılabilir. Domenic Denicola tarafından hazırlanan bir demoyu da deneyebilirsiniz.

Klasik History API basit görünse de çok iyi tanımlanmamıştır ve uç durumlar ile tarayıcılar arasında farklı şekilde uygulanmasıyla ilgili çok sayıda soruna sahiptir. Yeni Navigation API ile ilgili geri bildirimde bulunacağınızı umuyoruz.

Referanslar

Teşekkür ederiz

Bu yayını inceleyen Thomas Steiner, Domenic Denicola ve Nate Chapin'e teşekkür ederiz.