आधुनिक क्लाइंट-साइड रूटिंग: नेविगेशन एपीआई

नए एपीआई की मदद से क्लाइंट-साइड रूटिंग का स्टैंडर्ड तय करना, जो एक पेज के ऐप्लिकेशन बनाने में पूरी तरह से बदलाव करता है.

ब्राउज़र सहायता

  • 102
  • 102
  • x
  • x

Source

एक पेज के ऐप्लिकेशन या एसपीए एक मुख्य सुविधा होते हैं: वे सर्वर से नए पेज लोड करने के डिफ़ॉल्ट तरीके के बजाय, उपयोगकर्ता के साइट से इंटरैक्ट करने पर उनके कॉन्टेंट को डाइनैमिक तरीके से दोबारा लिखते हैं.

हालांकि, एसपीए, इतिहास एपीआई के ज़रिए आपको यह सुविधा दे पाए हैं (या कुछ मामलों में, साइट के #hash हिस्से में बदलाव करके), यह एसपीए से बहुत पहले तैयार किया गया अजीब एपीआई है. वेब एक बिलकुल नए तरीके का इस्तेमाल कर रहा है. नेविगेशन एपीआई एक ऐसा प्रस्तावित एपीआई है जो बस इतिहास एपीआई के किनारों को पैच करने की कोशिश के बजाय, इस स्पेस को पूरी तरह से बदल देता है. (उदाहरण के लिए, स्क्रोल रीस्टोरेशन ने इतिहास एपीआई में बदलाव करने के बजाय उसे पैच किया.)

इस पोस्ट में नेविगेशन एपीआई के बारे में बड़े लेवल पर जानकारी दी गई है. अगर आपको यह तकनीकी प्रस्ताव पढ़ना है, तो WICG के डेटा स्टोर करने की जगह में मौजूद ड्राफ़्ट रिपोर्ट देखें.

इस्तेमाल का उदाहरण

नेविगेशन एपीआई का इस्तेमाल करने के लिए, ग्लोबल navigation ऑब्जेक्ट पर "navigate" लिसनर जोड़कर शुरुआत करें. यह इवेंट, बुनियादी तौर पर एक ही जगह पर मौजूद होता है: यह सभी तरह के नेविगेशन के लिए ट्रिगर होगा.भले ही, उपयोगकर्ता ने कोई कार्रवाई (जैसे, किसी लिंक पर क्लिक करना, फ़ॉर्म सबमिट करना या वापस जाना) की हो या नेविगेशन को किसी प्रोग्राम के हिसाब से ट्रिगर किया गया हो. जैसे, आपकी साइट के कोड के ज़रिए. ज़्यादातर मामलों में, इसकी मदद से आपका कोड, उस कार्रवाई के लिए ब्राउज़र के डिफ़ॉल्ट व्यवहार को बदल देता है. एसपीए के लिए, इसका मतलब है कि उपयोगकर्ता को उसी पेज पर रखना और साइट का कॉन्टेंट लोड करना या बदलना.

"navigate" लिसनर को एक NavigateEvent भेजा जाता है, जिसमें नेविगेशन के बारे में जानकारी होती है, जैसे कि डेस्टिनेशन यूआरएल. इसकी मदद से, नेविगेशन का जवाब एक ही जगह से दिया जा सकता है. एक सामान्य "navigate" लिसनर ऐसा दिख सकता है:

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

आप इन दो में से किसी एक तरीके से नेविगेशन का काम कर सकते हैं:

  • नेविगेशन मैनेज करने के लिए, intercept({ handler }) (जैसा कि ऊपर बताया गया है) पर कॉल किया जा रहा है.
  • preventDefault() को कॉल किया जा रहा है. ऐसा करने पर, नेविगेशन पूरी तरह से रद्द हो जाएगा.

इस उदाहरण में, इवेंट के बारे में intercept() को कॉल किया गया है. ब्राउज़र आपके handler कॉलबैक को कॉल करता है, जिसे आपकी साइट की अगली स्थिति को कॉन्फ़िगर करना चाहिए. इससे एक ट्रांज़िशन ऑब्जेक्ट, navigation.transition बनता है. इस ऑब्जेक्ट का इस्तेमाल करके, नेविगेशन की प्रोग्रेस को ट्रैक किया जा सकता है.

आम तौर पर, intercept() और preventDefault(), दोनों का इस्तेमाल किया जा सकता है. हालांकि, कुछ मामलों में इन्हें कॉल नहीं किया जा सकता. अगर नेविगेशन, क्रॉस-ऑरिजिन नेविगेशन है, तो intercept() से नेविगेशन मैनेज नहीं किया जा सकता. साथ ही, अगर उपयोगकर्ता अपने ब्राउज़र में 'वापस जाएं' या 'आगे बढ़ें' बटन दबा रहा है, तो preventDefault() से नेविगेशन रद्द नहीं किया जा सकता. ऐसा करने से, उपयोगकर्ताओं को आपकी साइट पर नहीं रोका जा सकेगा. (इस बारे में GitHub पर चर्चा की जा रही है.)

भले ही, नेविगेशन को रोका या रोका न जा सके, लेकिन "navigate" इवेंट अब भी ट्रिगर होगा. यह जानकारी देने वाला है. उदाहरण के लिए, आपका कोड, Analytics के इवेंट को लॉग करके यह बता सकता है कि उपयोगकर्ता आपकी साइट छोड़कर जा रहा है.

प्लैटफ़ॉर्म में कोई दूसरा इवेंट क्यों जोड़ना चाहिए?

"navigate" इवेंट लिसनर, एसपीए में यूआरएल में होने वाले बदलावों को एक ही जगह से मैनेज करता है. पुराने एपीआई का इस्तेमाल करना मुश्किल है. अगर आपने कभी भी इतिहास एपीआई का इस्तेमाल करके अपने एसपीए के लिए रूटिंग लिखा है, तो हो सकता है कि आपने इस तरह का कोड जोड़ा हो:

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

यह सही है, लेकिन इसमें पूरी जानकारी नहीं है. आपके पेज पर लिंक आ सकते हैं और जा सकते हैं. साथ ही, उपयोगकर्ताओं के लिए सिर्फ़ यही एक तरीका नहीं है, जिससे वे पेजों पर नेविगेट कर सकें. उदाहरण के लिए, वे फ़ॉर्म सबमिट कर सकते हैं या इमेज मैप का इस्तेमाल भी कर सकते हैं. आपका पेज इन समस्याओं से निपट सकता है, लेकिन यहां संभावनाओं की एक लंबी सूची है जिसे बस और आसान बनाया जा सकता है—यह वह काम है जिसे नए नेविगेशन एपीआई से हासिल किया जा सकता है.

इसके अलावा, ऊपर दिए गए निर्देश, बैक-फ़ॉरवर्ड नेविगेशन को मैनेज नहीं करते. उसके लिए एक और इवेंट है, "popstate".

निजी तौर पर, इतिहास एपीआई को अक्सर ऐसा लगता है कि यह इन संभावनाओं को कम करने में मदद कर सकता है. हालांकि, इसमें पेज की सिर्फ़ दो जगहें हैं: उपयोगकर्ता के ब्राउज़र में 'वापस जाएं' या 'आगे जाएं' बटन दबाने पर कार्रवाई करने के साथ-साथ, यूआरएल को पुश करना और बदलना. इसमें "navigate" से कोई मिलता-जुलता नहीं है. हालांकि, जैसा कि ऊपर दिखाया गया है, वैसे ही क्लिक इवेंट के लिए मैन्युअल तरीके से लिसनर सेट करने पर ऐसा होता है.

नेविगेशन को मैनेज करने का तरीका तय करना

navigateEvent में नेविगेशन के बारे में काफ़ी जानकारी होती है. इसका इस्तेमाल करके किसी खास नेविगेशन को मैनेज करने का तरीका तय किया जा सकता है.

ये मुख्य प्रॉपर्टी हैं:

canIntercept
अगर यह गलत है, तो नेविगेशन को बीच में नहीं रोका जा सकता. क्रॉस-ऑरिजिन नेविगेशन और क्रॉस-डॉक्यूमेंट ट्रैवर्सल को बीच में नहीं रोका जा सकता.
destination.url
यह नेविगेशन को हैंडल करने के लिए सबसे ज़रूरी जानकारी है.
hashChange
अगर नेविगेशन एक ही दस्तावेज़ है और मौजूदा यूआरएल से अलग, सिर्फ़ हैश ही एक हिस्सा है, तो वैल्यू 'सही' होगी. मॉडर्न एसपीए में, हैश को मौजूदा दस्तावेज़ के अलग-अलग हिस्सों से लिंक करने के लिए होना चाहिए. अगर hashChange सही है, तो शायद आपको इस नेविगेशन को बीच में रोकने की ज़रूरत न हो.
downloadRequest
अगर यह बात सही है, तो download एट्रिब्यूट वाले लिंक से नेविगेशन शुरू किया गया था. ज़्यादातर मामलों में, आपको इससे बचने की ज़रूरत नहीं होती.
formData
अगर वैल्यू को शून्य नहीं किया गया है, तो यह नेविगेशन, पोस्ट फ़ॉर्म सबमिशन का हिस्सा है. नेविगेशन मैनेज करते समय इस बात का ध्यान रखें. अगर आपको सिर्फ़ जीईटी नेविगेशन मैनेज करना है, तो उन नेविगेशन को इंटरसेप्ट करने से बचें जहां formData शून्य नहीं है. बाद में लेख में, फ़ॉर्म सबमिशन को मैनेज करने का उदाहरण देखें.
navigationType
यह "reload", "push", "replace" या "traverse" में से कोई एक है. अगर यह "traverse" है, तो इस नेविगेशन को preventDefault() के ज़रिए रद्द नहीं किया जा सकता.

उदाहरण के लिए, पहले उदाहरण में इस्तेमाल किया गया shouldNotIntercept फ़ंक्शन, कुछ ऐसा हो सकता है:

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

इंटरसेप्ट कर रहा है

जब आपका कोड, "navigate" लिसनर से intercept({ handler }) को कॉल करता है, तो यह ब्राउज़र को बताता है कि अब वह पेज को नई और अपडेट की गई स्थिति के लिए तैयार कर रहा है. साथ ही, नेविगेशन में कुछ समय लग सकता है.

ब्राउज़र, मौजूदा स्थिति के लिए स्क्रोल की स्थिति कैप्चर करके शुरू करता है, ताकि बाद में इसे दूसरे तरीके से वापस लाया जा सके. इसके बाद, यह आपके handler कॉलबैक को कॉल करता है. अगर आपका handler प्रॉमिस लौटाता है (जो अपने-आप async functions के साथ होता है), तो वह प्रॉमिस ब्राउज़र को बताता है कि नेविगेशन में कितना समय लगता है और वह काम करता है या नहीं.

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

इसलिए, यह एपीआई एक सिमैंटिक सिद्धांत पेश करता है जिसे ब्राउज़र समझता है: एसपीए नेविगेशन, समय के साथ हो रहा होता है, जिससे दस्तावेज़ को पिछले यूआरएल और स्थिति से नए में बदल देता है. इसके कई फ़ायदे हैं. इनमें सुलभता भी शामिल है: ब्राउज़र किसी नेविगेशन के शुरू होने, खत्म होने या उसके न हो पाने की जानकारी दिखा सकते हैं. उदाहरण के लिए, Chrome अपने हिसाब से, कॉन्टेंट लोड होने का इंडिकेटर चालू करता है. साथ ही, इससे लोगों को 'बंद करें' बटन का इस्तेमाल करने की अनुमति मिलती है. (फ़िलहाल, जब उपयोगकर्ता बैक/फ़ॉरवर्ड बटन के ज़रिए नेविगेट करता है, तब ऐसा नहीं होता. हालांकि, इसे जल्द ही ठीक कर लिया जाएगा.)

नेविगेशन को इंटरसेप्ट करते समय, आपके handler कॉलबैक को कॉल करने से ठीक पहले नया यूआरएल लागू होगा. अगर डीओएम को तुरंत अपडेट नहीं किया जाता है, तो एक ऐसा पीरियड बन जाता है जिसमें पुराना कॉन्टेंट, नए यूआरएल के साथ दिखता है. इसका असर, डेटा फ़ेच करने या नए सबरिसॉर्स लोड करते समय, मिलते-जुलते यूआरएल का रिज़ॉल्यूशन जैसी चीज़ों पर पड़ता है.

यूआरएल बदलने में हो रही देरी का तरीका GitHub पर बताया जा रहा है, लेकिन आम तौर पर हमारा सुझाव है कि आने वाले कॉन्टेंट के लिए, पेज को तुरंत किसी तरह के प्लेसहोल्डर से अपडेट कर दें:

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

इससे न सिर्फ़ यूआरएल की समस्याओं से बचा जा सकता है, बल्कि तेज़ी से भी मदद मिल सकती है, क्योंकि आप उपयोगकर्ता को तुरंत जवाब दे रहे हैं.

सिग्नल रद्द करें

intercept() हैंडलर में एसिंक्रोनस काम किया जा सकता है. इसलिए, हो सकता है कि नेविगेशन की ज़रूरत न पड़े. ऐसा तब होता है, जब:

  • उपयोगकर्ता किसी दूसरे लिंक पर क्लिक करता है या कुछ कोड दूसरे नेविगेशन का इस्तेमाल करते हैं. इस मामले में, नए नेविगेशन के लिए पुराने नेविगेशन को छोड़ दिया जाता है.
  • उपयोगकर्ता, ब्राउज़र में 'रोकें' बटन पर क्लिक करता है.

इनमें से किसी भी संभावना से निपटने के लिए, "navigate" लिसनर को भेजे गए इवेंट में एक signal प्रॉपर्टी होती है, जो एक AbortSignal होती है. ज़्यादा जानकारी के लिए, डेटा फ़ेच करने की प्रोसेस रद्द करना लेख पढ़ें.

कम शब्दों में यह बेहद ज़रूरी है कि यह एक ऐसा ऑब्जेक्ट उपलब्ध कराता हो जो काम को रोकने के बाद इवेंट ट्रिगर करता है. ध्यान दें, fetch() पर की जाने वाली किसी भी कॉल के लिए, AbortSignal पास किया जा सकता है. इससे, नेविगेशन को पहले ही रद्द करने पर इन-फ़्लाइट नेटवर्क के अनुरोध रद्द हो जाएंगे. इससे उपयोगकर्ता का बैंडविथ सेव हो जाएगा और fetch() से लौटाए गए Promise को अस्वीकार कर दिया जाएगा. साथ ही, नीचे दिए गए किसी भी कोड को, DOM को अपडेट करने जैसी कार्रवाइयों से रोका जा सकेगा, ताकि वह अभी अमान्य पेज नेविगेशन दिखा सके.

यहां दिया गया पिछला उदाहरण दिया गया है, लेकिन इसमें getArticleContent की लाइन है. इसमें दिखाया गया है कि fetch() के साथ AbortSignal का इस्तेमाल कैसे किया जा सकता है:

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

स्क्रोल हैंडलिंग

जब नेविगेशन को intercept() किया जाता है, तो ब्राउज़र अपने-आप स्क्रोल होने की सुविधा को मैनेज करने की कोशिश करता है.

इतिहास की नई एंट्री (जब navigationEvent.navigationType, "push" या "replace" हो) पर ले जाने वाले नेविगेशन के लिए, इसका मतलब है कि यूआरएल फ़्रैगमेंट (# के बाद का थोड़ा) से बताए गए हिस्से तक स्क्रोल करने या स्क्रोल को पेज के सबसे ऊपरी हिस्से पर रीसेट करने की कोशिश की जा रही है.

फिर से लोड करने और ट्रैवर्सल के लिए, इसका मतलब है कि स्क्रोल करने की पोज़िशन को वापस उस जगह पर वापस लाया जा सकता है जहां पर इस इतिहास की एंट्री को पिछली बार दिखाया गया था.

डिफ़ॉल्ट रूप से, ऐसा आपके handler से मिलने वाला प्रॉमिस रिज़ॉल्व होने पर होता है. हालांकि, अगर पहले स्क्रोल करना सही लगता है, तो 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);
      },
    });
  }
});

इसके अलावा, आपके पास intercept() के scroll विकल्प को "manual" पर सेट करके, स्क्रोल अपने-आप मैनेज होने की सुविधा से पूरी तरह ऑप्ट आउट करने का विकल्प भी है:

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

फ़ोकस मैनेज करना

आपके handler से मिलने वाले प्रॉमिस का समाधान हो जाने पर, ब्राउज़र autofocus एट्रिब्यूट सेट वाले पहले एलिमेंट पर फ़ोकस करेगा. इसके अलावा, अगर किसी एलिमेंट में एट्रिब्यूट नहीं है, तो <body> एलिमेंट पर फ़ोकस करेगा.

इस व्यवहार से ऑप्ट आउट करने के लिए, intercept() के focusReset विकल्प को "manual" पर सेट करें:

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

सफलता और विफलता इवेंट

जब आपके intercept() हैंडलर को कॉल किया जाता है, तो इनमें से कोई एक काम होगा:

  • अगर लौटाया गया Promise अनुरोध पूरा करता है (या आपने intercept() को कॉल नहीं किया), तो नेविगेशन एपीआई, Event के साथ "navigatesuccess" को सक्रिय करेगा.
  • अगर दिखाया गया Promise अस्वीकार करता है, तो एपीआई, ErrorEvent के साथ "navigateerror" को ट्रिगर करेगा.

इन इवेंट की मदद से, आपका कोड एक ही जगह पर सफलता या असफलता से निपटने में मदद कर सकता है. उदाहरण के लिए, हो सकता है कि आप पहले दिखाए गए प्रोग्रेस इंडिकेटर को छिपाकर, सफलता से निपट सकें, जैसे कि:

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

इसके अलावा, फ़ेल होने पर गड़बड़ी का मैसेज दिखाया जा सकता है:

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

ErrorEvent पाने वाले "navigateerror" इवेंट लिसनर को खास तौर पर, इस्तेमाल में मदद मिलती है, क्योंकि इससे यह गारंटी मिलती है कि नया पेज सेट अप करने वाले कोड से कोई गड़बड़ी होगी. await fetch() को पता है कि नेटवर्क उपलब्ध न होने पर, गड़बड़ी को "navigateerror" पर रूट कर दिया जाएगा.

navigation.currentEntry से मौजूदा एंट्री का ऐक्सेस मिलता है. यह एक ऑब्जेक्ट है, जो बताता है कि उपयोगकर्ता अभी कहां है. इस एंट्री में मौजूदा यूआरएल, ऐसा मेटाडेटा जिसकी मदद से समय के साथ इस एंट्री की पहचान की जा सकती है, और डेवलपर की ओर से दी गई स्थिति शामिल होती है.

मेटाडेटा में key शामिल है. यह हर एंट्री की यूनीक स्ट्रिंग प्रॉपर्टी होती है, जो मौजूदा एंट्री और उसके स्लॉट के बारे में बताती है. मौजूदा एंट्री के यूआरएल या स्थिति में बदलाव होने पर भी, यह पासकोड वही रहता है. यह अब भी उसी स्लॉट में है. इसके उलट, अगर कोई उपयोगकर्ता वापस जाएं बटन को दबाकर उसी पेज को दोबारा खोलता है, तो key बदल जाएगा. इसकी वजह यह है कि इस नई एंट्री से नया स्लॉट बन जाता है.

डेवलपर के लिए, key उपयोगी है, क्योंकि नेविगेशन एपीआई की मदद से उपयोगकर्ता को सीधे किसी मिलती-जुलती कुंजी वाली एंट्री पर नेविगेट किया जा सकता है. आप एक पेज से दूसरे पेज पर आसानी से जाने के लिए, बाकी एंट्री की स्थितियों में भी इसे होल्ड पर रख सकते हैं.

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

स्थिति

नेविगेशन एपीआई से "स्थिति" का एक संकेत दिखता है. यह डेवलपर की ओर से दी गई जानकारी होती है, जिसे मौजूदा इतिहास की एंट्री में लगातार सेव किया जाता है, लेकिन यह उपयोगकर्ता को सीधे तौर पर नहीं दिखती. यह सुविधा काफ़ी हद तक इतिहास एपीआई में मौजूद history.state से काफ़ी मिलती-जुलती है. हालांकि, इसमें सुधार किया गया है.

नेविगेशन एपीआई में, मौजूदा एंट्री (या किसी भी एंट्री) के .getState() तरीके को कॉल करके उसकी स्थिति की कॉपी दी जा सकती है:

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

डिफ़ॉल्ट रूप से, यह undefined होगा.

सेटिंग स्थिति

हालांकि स्टेट ऑब्जेक्ट में बदलाव किया जा सकता है, लेकिन उन बदलावों को इतिहास एंट्री में वापस सेव नहीं किया जाता, इसलिए:

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

स्थिति सेट करने का सही तरीका, स्क्रिप्ट नेविगेशन के दौरान है:

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

जहां newState, कोई भी क्लोन करने लायक ऑब्जेक्ट हो सकता है.

अगर आपको मौजूदा एंट्री की स्थिति अपडेट करनी है, तो मौजूदा एंट्री को बदलने वाला नेविगेशन इस्तेमाल करना सबसे अच्छा रहेगा:

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

इसके बाद, आपका "navigate" इवेंट लिसनर navigateEvent.destination के ज़रिए इस बदलाव को सुन सकता है:

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

स्थिति को सिंक्रोनस रूप से अपडेट किया जा रहा है

आम तौर पर, navigation.reload({state: newState}) से एसिंक्रोनस तरीके से स्टेटस अपडेट करना बेहतर होता है. हालांकि, आपका "navigate" लिसनर उस स्टेट को लागू कर सकता है. हालांकि, कभी-कभी कोड को सूचना मिलने से पहले ही, कोड की स्थिति में किया गया बदलाव पूरी तरह लागू हो जाता है. जैसे, जब उपयोगकर्ता किसी <details> एलिमेंट को टॉगल करता है या उपयोगकर्ता किसी फ़ॉर्म के इनपुट का स्टेटस बदलता है. इन मामलों में, हो सकता है कि आप स्थिति को अपडेट करना चाहें, ताकि ये बदलाव फिर से लोड होने और ट्रैवर्सल के ज़रिए सुरक्षित रहें. updateCurrentEntry() का इस्तेमाल करके ऐसा किया जा सकता है:

navigation.updateCurrentEntry({state: newState});

इस बदलाव के बारे में जानने के लिए यहां एक इवेंट भी दिया गया है:

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

हालांकि, अगर "currententrychange" में स्थिति के बदलावों पर प्रतिक्रिया दी जाती है, तो "navigate" इवेंट और "currententrychange" इवेंट के बीच में अपने स्टेट कोड को बांटा जा सकता है या इसकी डुप्लीकेट कॉपी बनाई जा सकती है. वहीं, navigation.reload({state: newState}) की मदद से इसे एक ही जगह पर मैनेज किया जा सकता है.

स्थिति बनाम यूआरएल पैरामीटर

राज्य कोई स्ट्रक्चर्ड ऑब्जेक्ट हो सकता है, इसलिए अपने ऐप्लिकेशन की सभी स्थिति के लिए इसका इस्तेमाल करना दिलचस्प होगा. हालांकि, कई मामलों में उस स्थिति को यूआरएल में सेव करना बेहतर होता है.

अगर आपको लगता है कि यह स्थिति बनी ही रहेगी और उपयोगकर्ता किसी दूसरे उपयोगकर्ता के साथ यूआरएल शेयर करेगा, तो इस स्थिति को यूआरएल में सेव करें. नहीं तो, स्टेट ऑब्जेक्ट बेहतर विकल्प है.

सभी एंट्री ऐक्सेस करें

हालांकि, "मौजूदा एंट्री" सभी कार्रवाइयां नहीं करती हैं. एपीआई की मदद से, एंट्री की पूरी सूची को ऐक्सेस किया जा सकता है. इस सूची में उन सभी एंट्री को ऐक्सेस किया जा सकता है जिन्हें उपयोगकर्ता ने आपकी साइट पर navigation.entries() कॉल के दौरान नेविगेट किया था. इससे, एंट्री के स्नैपशॉट का कलेक्शन दिखता है. इसका इस्तेमाल कई कामों के लिए किया जा सकता है. उदाहरण के लिए, किसी पेज पर उपयोगकर्ता के नेविगेट किए जाने के तरीके के आधार पर कोई दूसरा यूज़र इंटरफ़ेस (यूआई) दिखाना या पिछले यूआरएल या उनकी स्थितियों को देखना. मौजूदा इतिहास एपीआई की मदद से ऐसा नहीं किया जा सकता.

आपके पास अलग-अलग NavigationHistoryEntry पर भी "dispose" इवेंट सुनने का विकल्प है. यह इवेंट तब ट्रिगर होता है, जब एंट्री, ब्राउज़िंग इतिहास का हिस्सा नहीं होती. ऐसा सामान्य क्लीनअप के दौरान हो सकता है, लेकिन नेविगेट करते समय भी ऐसा हो सकता है. उदाहरण के लिए, अगर आप 10 जगहें पीछे जाते हैं, तो आगे की ओर जाते हैं, तो इतिहास की वे 10 एंट्री मिट जाती हैं.

उदाहरण

जैसा कि ऊपर बताया गया है, "navigate" इवेंट सभी तरह के नेविगेशन के लिए ट्रिगर होता है. (वास्तव में, सभी संभावित प्रकारों की जानकारी में एक लंबा अपेंडिक्स दिया गया है.)

हालांकि, कई साइटों के मामले में सबसे ज़्यादा तब सामने आते हैं, जब उपयोगकर्ता <a href="..."> पर क्लिक करता है. फिर भी, दो अहम और ज़्यादा जटिल नेविगेशन टाइप होते हैं, जो कवर करने लायक हैं.

प्रोग्राम के हिसाब से, अपने-आप नेविगेट करने की सुविधा

पहला प्रोग्राम, प्रोग्राम के हिसाब से नेविगेशन करना है. इसमें क्लाइंट-साइड कोड को कॉल करने की वजह से नेविगेशन होता है.

नेविगेशन का इस्तेमाल करने के लिए, अपने कोड में कहीं से भी navigation.navigate('/another_page') को कॉल करें. इसे "navigate" लिसनर पर रजिस्टर किया गया, सेंट्रलाइज़्ड इवेंट लिसनर मैनेज करेगा. साथ ही, आपके सेंट्रलाइज़्ड लिसनर को सिंक्रोनस (एक साथ सिंक करने के लिए) कहा जाएगा.

यह पुराने तरीकों, जैसे कि location.assign() और दोस्तों के साथ-साथ इतिहास एपीआई के तरीकों pushState() और replaceState() के बेहतर एग्रीगेशन के तौर पर बनाया गया है.

navigation.navigate() तरीका ऐसा ऑब्जेक्ट दिखाता है जिसमें { committed, finished } में दो Promise इंस्टेंस होते हैं. इससे, शुरू करने वाला व्यक्ति तब तक इंतज़ार कर सकता है, जब तक कि ट्रांज़िशन "किए जाने" (दिखने वाला यूआरएल बदल गया है और नया NavigationHistoryEntry उपलब्ध है) या "पूरा" हो जाता है (intercept({ handler }) से लौटाए गए सभी प्रॉमिस पूरे हो जाते हैं—या किसी दूसरे नेविगेशन से होने से पहले ही रद्द हो जाने की वजह से पूरे या अस्वीकार हो जाते हैं).

navigate तरीके में एक विकल्प ऑब्जेक्ट भी होता है, जहां यह सेट किया जा सकता है:

  • state: नई इतिहास की एंट्री की स्थिति, जैसा कि NavigationHistoryEntry पर .getState() तरीके से उपलब्ध होता है.
  • history: मौजूदा इतिहास की एंट्री को बदलने के लिए, इसे "replace" पर सेट किया जा सकता है.
  • info: navigateEvent.info के ज़रिए नेविगेट इवेंट को पास करने के लिए एक ऑब्जेक्ट.

उदाहरण के लिए, info से किसी ऐसे ऐनिमेशन को दिखाने में मदद मिल सकती है जिसकी वजह से अगला पेज दिखता है. इसका दूसरा विकल्प ग्लोबल वैरिएबल को सेट करना या इसे #hash के हिस्से के तौर पर शामिल करना हो सकता है. दोनों विकल्प थोड़े अजीब हैं.) ध्यान दें कि अगर किसी उपयोगकर्ता ने बाद में नेविगेशन चालू किया है, तो इस info को फिर से नहीं चलाया जा सकेगा. उदाहरण के लिए, 'वापस जाएं' और 'आगे बढ़ें' बटन की मदद से. दरअसल, ऐसे मामलों में यह हमेशा undefined होगा.

बाईं या दाईं ओर से खुलने का डेमो

navigation में नेविगेशन के और भी कई तरीके होते हैं. ये सभी तरीके { committed, finished } वाला ऑब्जेक्ट दिखाते हैं. मैंने traverseTo() और navigate() के बारे में पहले ही बता दिया है. यह key को स्वीकार करता है, जो उपयोगकर्ता के इतिहास में किसी खास एंट्री को दिखाता है. इसमें back(), forward(), और reload() भी शामिल हैं. navigate() की तरह ही, इन सभी तरीकों को "navigate" इवेंट लिसनर की मदद से मैनेज किया जाता है.

फ़ॉर्म सबमिशन

इसके अलावा, पीओएसटी तरीके से एचटीएमएल <form> सबमिट करना एक खास तरह का नेविगेशन सिस्टम है. नेविगेशन एपीआई इसे बीच में रोक सकता है. इसमें एक अतिरिक्त पेलोड शामिल है, लेकिन "navigate" लिसनर अब भी नेविगेशन को एक ही जगह से हैंडल करता है.

NavigateEvent पर formData प्रॉपर्टी को खोजकर, फ़ॉर्म सबमिशन का पता लगाया जा सकता है. यहां एक उदाहरण दिया गया है, जिसमें किसी भी फ़ॉर्म सबमिशन को एक ऐसे फ़ॉर्म में बदल दिया जाता है जो 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"
      },
    });
  }
});

क्‍या अनुपलब्‍ध है?

"navigate" इवेंट लिसनर की एक ही जगह पर होने के बावजूद, नेविगेशन एपीआई की मौजूदा स्पेसिफ़िकेशन, पेज के पहले लोड पर "navigate" को ट्रिगर नहीं करती. साथ ही, सभी राज्यों के लिए सर्वर साइड रेंडरिंग (एसएसआर) का इस्तेमाल करने वाली साइटों के लिए यह ठीक हो सकता है. आपका सर्वर सही शुरुआती स्थिति दिखाएगा, जो आपके उपयोगकर्ताओं तक कॉन्टेंट पहुंचाने का सबसे तेज़ तरीका है. हालांकि, क्लाइंट-साइड कोड का इस्तेमाल करके अपने पेज बनाने वाली साइटों को अपने पेज शुरू करने के लिए, एक और फ़ंक्शन बनाना पड़ सकता है.

नेविगेशन एपीआई को डिज़ाइन करने के लिए एक और विकल्प यह भी है कि यह सिर्फ़ एक फ़्रेम में काम करता है. जैसे, टॉप लेवल पेज या किसी एक खास <iframe> पर. इसके कई दिलचस्प नतीजे हैं, जो इस जानकारी में आगे बताए गए हैं. हालांकि, इससे डेवलपर के लिए भ्रम को कम करने में मदद मिलेगी. इतिहास के पुराने एपीआई में किनारे के कई मामले होते हैं जिनमें भ्रम की स्थिति पैदा होती है. जैसे कि फ़्रेम के लिए सहायता और नए सिरे से तैयार किया गया नेविगेशन एपीआई, इन किनारे वाले मामलों को शुरुआत से ही ठीक कर देता है.

आखिर में, उपयोगकर्ता ने जिन एंट्री को नेविगेट किया है उनकी सूची में प्रोग्राम के हिसाब से बदलाव करने या उसे फिर से व्यवस्थित करने पर अभी तक आम सहमति नहीं है. इस पर फ़िलहाल बातचीत चल रही है. हालांकि, हो सकता है कि सिर्फ़ डेटा मिटाने की अनुमति दी जाए: पुरानी एंट्री या "आने वाले समय की सभी एंट्री". बाद वाली स्थिति में, कुछ समय के लिए रोक लगाई जा सकती है. उदाहरण, एक डेवलपर के तौर पर, मैं ये काम कर सकता/सकती हूं:

  • नए यूआरएल या स्थिति पर जाकर, उपयोगकर्ता से सवाल पूछें
  • उपयोगकर्ता को अपना काम पूरा करने की अनुमति दें (या वापस जाएं)
  • टास्क पूरा होने पर इतिहास की एंट्री हटाएं

यह कुछ समय के लिए काम करने वाले मॉडल या पेज पर अचानक दिखने वाले विज्ञापनों के लिए सही हो सकता है: नया यूआरएल ऐसा होता है जिसे उपयोगकर्ता, पेज छोड़ने के लिए 'वापस जाएं' जेस्चर का इस्तेमाल कर सकते हैं. हालांकि, वे गलती से इसे फिर से खोलने के लिए 'आगे बढ़ें' पर नहीं जा सकते (क्योंकि एंट्री हटा दी गई है). मौजूदा इतिहास एपीआई के साथ ऐसा नहीं किया जा सकता.

नेविगेशन एपीआई आज़माएं

नेविगेशन एपीआई, Chrome 102 में बिना फ़्लैग के उपलब्ध है. आपके पास डॉमिनिक डेनिकोला का डेमो देखने का विकल्प भी है.

हालांकि, क्लासिक हिस्ट्री एपीआई तो बिलकुल आसान है, लेकिन इसकी अच्छी तरह से जानकारी नहीं दी गई है. इसमें कोने वाले मामलों में बहुत सारी समस्याएं आ रही हैं. साथ ही, यह भी पता चलता है कि इसे सभी ब्राउज़र में अलग-अलग तरीके से कैसे लागू किया गया है. हमें उम्मीद है कि आप नए नेविगेशन एपीआई के बारे में अपने सुझाव/राय देंगे या शिकायत करेंगे.

रेफ़रंस

स्वीकार की गई

इस पोस्ट की समीक्षा करने के लिए थॉमस स्टाइनर, डोमिनिक डेनिकोला, और नेट चैपिन को धन्यवाद. जेरेमी ज़ीरो की किताब Unस्प्लैश से हीरो इमेज.