আধুনিক ক্লায়েন্ট-সাইড রাউটিং: নেভিগেশন API

একটি একেবারে নতুন API এর মাধ্যমে ক্লায়েন্ট-সাইড রাউটিংকে মানক করা যা একক-পৃষ্ঠার অ্যাপ্লিকেশনগুলিকে সম্পূর্ণরূপে ওভারহল করে।

ব্রাউজার সমর্থন

  • 102
  • 102
  • এক্স
  • এক্স

উৎস

একক-পৃষ্ঠা অ্যাপ্লিকেশন, বা SPA, একটি মূল বৈশিষ্ট্য দ্বারা সংজ্ঞায়িত করা হয়: সার্ভার থেকে সম্পূর্ণ নতুন পৃষ্ঠাগুলি লোড করার ডিফল্ট পদ্ধতির পরিবর্তে ব্যবহারকারী সাইটের সাথে ইন্টারঅ্যাক্ট করার সাথে সাথে তাদের বিষয়বস্তুকে গতিশীলভাবে পুনরায় লেখা।

যদিও SPA গুলি আপনাকে History API এর মাধ্যমে এই বৈশিষ্ট্যটি আনতে সক্ষম হয়েছে (বা সীমিত ক্ষেত্রে, সাইটের #hash অংশ সামঞ্জস্য করে), এটি একটি ক্লাঙ্ক এপিআই যা অনেক আগে SPA গুলি আদর্শ ছিল-এবং ওয়েব একটি জন্য চিৎকার করছে সম্পূর্ণ নতুন পদ্ধতি। ন্যাভিগেশন এপিআই হল একটি প্রস্তাবিত এপিআই যা ইতিহাস এপিআই-এর রুক্ষ প্রান্তগুলিকে সহজভাবে প্যাচ করার চেষ্টা করার পরিবর্তে এই স্থানটিকে সম্পূর্ণভাবে ওভারহল করে। (উদাহরণস্বরূপ, স্ক্রোল পুনরুদ্ধার এটিকে পুনরায় উদ্ভাবনের চেষ্টা করার পরিবর্তে ইতিহাস API প্যাচ করেছে।)

এই পোস্টটি একটি উচ্চ স্তরে নেভিগেশন API বর্ণনা করে। আপনি যদি প্রযুক্তিগত প্রস্তাবটি পড়তে চান, WICG সংগ্রহস্থলে খসড়া প্রতিবেদনটি দেখুন

উদাহরণ ব্যবহার

ন্যাভিগেশন API ব্যবহার করতে, গ্লোবাল navigation অবজেক্টে একটি "navigate" লিসেনার যোগ করে শুরু করুন। এই ইভেন্টটি মৌলিকভাবে কেন্দ্রীভূত : এটি সমস্ত ধরণের নেভিগেশনের জন্য ফায়ার করবে, ব্যবহারকারী একটি ক্রিয়া সম্পাদন করেছে কিনা (যেমন একটি লিঙ্কে ক্লিক করা, একটি ফর্ম জমা দেওয়া, বা পিছনে এবং এগিয়ে যাওয়া) বা যখন নেভিগেশন প্রোগ্রামগতভাবে ট্রিগার করা হয় (অর্থাৎ, আপনার সাইটের মাধ্যমে কোড)। বেশিরভাগ ক্ষেত্রে, এটি আপনার কোডটিকে সেই কর্মের জন্য ব্রাউজারের ডিফল্ট আচরণকে ওভাররাইড করতে দেয়। SPA-এর জন্য, এর অর্থ সম্ভবত ব্যবহারকারীকে একই পৃষ্ঠায় রাখা এবং সাইটের সামগ্রী লোড করা বা পরিবর্তন করা।

একটি NavigateEvent "navigate" শ্রোতার কাছে প্রেরণ করা হয় যাতে নেভিগেশন সম্পর্কিত তথ্য থাকে, যেমন গন্তব্য URL, এবং আপনাকে একটি কেন্দ্রীভূত স্থানে নেভিগেশনে প্রতিক্রিয়া জানাতে দেয়৷ একটি মৌলিক "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() এর মাধ্যমে একটি নেভিগেশন বাতিল করতে পারবেন না; আপনি আপনার সাইটে আপনার ব্যবহারকারীদের ফাঁদ করতে সক্ষম হবেন না. (এটি গিটহাবে আলোচনা করা হচ্ছে ।)

এমনকি আপনি যদি নেভিগেশন নিজেই থামাতে বা বাধা দিতে না পারেন, "navigate" ইভেন্টটি চালু হবে। এটি তথ্যপূর্ণ , তাই আপনার কোড, উদাহরণস্বরূপ, একটি অ্যানালিটিক্স ইভেন্ট লগ করতে পারে ইঙ্গিত করতে যে একজন ব্যবহারকারী আপনার সাইট ছেড়ে যাচ্ছেন৷

কেন প্ল্যাটফর্মে অন্য ইভেন্ট যোগ করুন?

একটি "navigate" ইভেন্ট শ্রোতা একটি SPA-এর মধ্যে ইউআরএল পরিবর্তন পরিচালনাকে কেন্দ্রীভূত করে। এটি পুরানো API ব্যবহার করে একটি কঠিন প্রস্তাব। আপনি যদি কখনও ইতিহাস API ব্যবহার করে আপনার নিজের SPA-এর জন্য রাউটিং লিখে থাকেন, তাহলে আপনি এইরকম কোড যোগ করতে পারেন:

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

এই জরিমানা, কিন্তু সম্পূর্ণ নয়. লিঙ্কগুলি আপনার পৃষ্ঠায় আসতে পারে এবং যেতে পারে এবং ব্যবহারকারীরা পৃষ্ঠাগুলির মাধ্যমে নেভিগেট করার একমাত্র উপায় নয়৷ উদাহরণস্বরূপ, তারা একটি ফর্ম জমা দিতে পারে বা এমনকি একটি চিত্র মানচিত্র ব্যবহার করতে পারে। আপনার পৃষ্ঠা এইগুলির সাথে মোকাবিলা করতে পারে, তবে সম্ভাবনার একটি দীর্ঘ লেজ রয়েছে যা কেবল সরলীকৃত হতে পারে—এমন কিছু যা নতুন নেভিগেশন API অর্জন করে।

অতিরিক্তভাবে, উপরেরটি পিছনে/ফরোয়ার্ড নেভিগেশন পরিচালনা করে না। এর জন্য আরেকটি ইভেন্ট আছে, "popstate"

ব্যক্তিগতভাবে, ইতিহাস API প্রায়শই মনে করে যে এটি এই সম্ভাবনাগুলির সাথে সাহায্য করার জন্য কিছু পথ যেতে পারে। যাইহোক, এটিতে সত্যিই দুটি পৃষ্ঠের ক্ষেত্র রয়েছে: ব্যবহারকারী যদি তাদের ব্রাউজারে পিছনে বা ফরোয়ার্ড চাপেন তবে প্রতিক্রিয়া জানানো এবং URL পুশ করা এবং প্রতিস্থাপন করা। এটিতে "navigate" এর কোনো সাদৃশ্য নেই, যদি আপনি নিজে নিজে ক্লিক ইভেন্টের জন্য শ্রোতাদের সেট আপ করেন, যেমন উপরে দেখানো হয়েছে।

একটি নেভিগেশন পরিচালনা কিভাবে সিদ্ধান্ত

navigateEvent নেভিগেশন সম্পর্কে অনেক তথ্য রয়েছে যা আপনি একটি নির্দিষ্ট নেভিগেশনের সাথে কীভাবে মোকাবিলা করবেন তা নির্ধারণ করতে ব্যবহার করতে পারেন।

মূল বৈশিষ্ট্য হল:

canIntercept
এটি মিথ্যা হলে, আপনি নেভিগেশন বাধা দিতে পারবেন না। ক্রস-অরিজিন নেভিগেশন এবং ক্রস-ডকুমেন্ট ট্রাভার্সালগুলিকে আটকানো যাবে না।
destination.url
সম্ভবত নেভিগেশন পরিচালনা করার সময় বিবেচনা করা তথ্যের সবচেয়ে গুরুত্বপূর্ণ অংশ।
hashChange
যদি নেভিগেশন একই-ডকুমেন্ট হয়, এবং হ্যাশ হল URL এর একমাত্র অংশ যা বর্তমান URL থেকে আলাদা। আধুনিক এসপিএ-তে, বর্তমান নথির বিভিন্ন অংশে লিঙ্ক করার জন্য হ্যাশ হওয়া উচিত। সুতরাং, যদি hashChange সত্য হয়, তাহলে আপনাকে সম্ভবত এই নেভিগেশনটি আটকাতে হবে না।
downloadRequest
যদি এটি সত্য হয়, একটি download বৈশিষ্ট্য সহ একটি লিঙ্ক দ্বারা নেভিগেশন শুরু হয়েছিল৷ বেশিরভাগ ক্ষেত্রে, আপনাকে এটিকে বাধা দিতে হবে না।
formData
যদি এটি শূন্য না হয়, তাহলে এই নেভিগেশনটি একটি POST ফর্ম জমা দেওয়ার অংশ৷ নেভিগেশন পরিচালনা করার সময় আপনি এটি অ্যাকাউন্টে নিতে ভুলবেন না। আপনি যদি শুধুমাত্র GET নেভিগেশনগুলি পরিচালনা করতে চান, যেখানে 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 একটি প্রতিশ্রুতি ফেরত দেয় (যা স্বয়ংক্রিয়ভাবে অ্যাসিঙ্ক ফাংশনগুলির সাথে ঘটে), সেই প্রতিশ্রুতিটি ব্রাউজারকে বলে নেভিগেশন কতক্ষণ সময় নেয় এবং এটি সফল কিনা।

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

যেমন, এই APIটি একটি শব্দার্থিক ধারণা উপস্থাপন করে যা ব্রাউজার বোঝে: একটি SPA নেভিগেশন বর্তমানে ঘটছে, সময়ের সাথে সাথে, একটি পূর্ববর্তী URL এবং অবস্থা থেকে একটি নতুন নথিতে পরিবর্তন করে৷ অ্যাক্সেসিবিলিটি সহ এটির অনেকগুলি সম্ভাব্য সুবিধা রয়েছে: ব্রাউজারগুলি কোনও নেভিগেশনের শুরু, শেষ বা সম্ভাব্য ব্যর্থতা দেখাতে পারে। ক্রোম, উদাহরণস্বরূপ, তার নেটিভ লোডিং সূচক সক্রিয় করে এবং ব্যবহারকারীকে স্টপ বোতামের সাথে ইন্টারঅ্যাক্ট করার অনুমতি দেয়। (ব্যবহারকারী ব্যাক/ফরওয়ার্ড বোতামগুলির মাধ্যমে নেভিগেট করার সময় এটি বর্তমানে ঘটবে না, তবে এটি শীঘ্রই ঠিক করা হবে ।)

নেভিগেশন বাধা দেওয়ার সময়, আপনার handler কলব্যাক কল করার ঠিক আগে নতুন URL কার্যকর হবে। আপনি অবিলম্বে DOM আপডেট না করলে, এটি একটি সময়কাল তৈরি করে যেখানে নতুন URL এর সাথে পুরানো বিষয়বস্তু প্রদর্শিত হয়। ডেটা আনার সময় বা নতুন সাবরিসোর্স লোড করার সময় এটি আপেক্ষিক URL রেজোলিউশনের মতো জিনিসগুলিকে প্রভাবিত করে।

ইউআরএল পরিবর্তন বিলম্বিত করার একটি উপায় 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 । আরও তথ্যের জন্য বাতিলযোগ্য আনা দেখুন।

সংক্ষিপ্ত সংস্করণ হল এটি মূলত এমন একটি বস্তু সরবরাহ করে যা একটি ইভেন্টকে আগুন দেয় যখন আপনার কাজ বন্ধ করা উচিত। উল্লেখযোগ্যভাবে, আপনি একটি AbortSignal পাস করতে পারেন যে কোনো কলে আপনি fetch() , যা নেভিগেশন অগ্রিম করা হলে ইন-ফ্লাইট নেটওয়ার্ক অনুরোধগুলি বাতিল করবে। এটি উভয়ই ব্যবহারকারীর ব্যান্ডউইথ সংরক্ষণ করবে, এবং fetch() দ্বারা প্রত্যাবর্তিত Promise প্রত্যাখ্যান করবে, এখনকার একটি অবৈধ পৃষ্ঠা নেভিগেশন দেখানোর জন্য DOM আপডেট করার মতো কাজ থেকে নিম্নলিখিত কোডগুলিকে বাধা দেবে৷

এখানে পূর্ববর্তী উদাহরণ, কিন্তু getArticleContent ইনলাইন সহ, দেখানো হচ্ছে কিভাবে AbortSignal 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);
      },
    });
  }
});

স্ক্রোল হ্যান্ডলিং

আপনি যখন একটি নেভিগেশন 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 প্রত্যাখ্যান করে, API একটি 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}`);
});

"navigateerror" ইভেন্ট শ্রোতা, যেটি একটি ErrorEvent প্রাপ্ত করে, বিশেষভাবে সুবিধাজনক কারণ এটি একটি নতুন পৃষ্ঠা সেট আপ করার জন্য আপনার কোড থেকে কোনো ত্রুটি পাওয়ার নিশ্চয়তা দেয়৷ আপনি কেবল await fetch() পারেন এই জেনে যে নেটওয়ার্কটি অনুপলব্ধ হলে, ত্রুটিটি শেষ পর্যন্ত "navigateerror" এ রুট করা হবে।

navigation.currentEntry বর্তমান এন্ট্রি অ্যাক্সেস প্রদান করে. এটি এমন একটি বস্তু যা বর্ণনা করে যে ব্যবহারকারী এখন কোথায় আছেন। এই এন্ট্রিতে বর্তমান URL, মেটাডেটা যা সময়ের সাথে সাথে এই এন্ট্রি সনাক্ত করতে ব্যবহার করা যেতে পারে এবং ডেভেলপার-প্রদত্ত অবস্থা অন্তর্ভুক্ত করে।

মেটাডেটা key অন্তর্ভুক্ত করে, প্রতিটি এন্ট্রির একটি অনন্য স্ট্রিং বৈশিষ্ট্য যা বর্তমান এন্ট্রি এবং এর স্লটকে প্রতিনিধিত্ব করে। বর্তমান এন্ট্রির URL বা অবস্থা পরিবর্তন হলেও এই কী একই থাকে। এটি এখনও একই স্লটে রয়েছে। বিপরীতভাবে, যদি একজন ব্যবহারকারী Back চাপেন এবং তারপর একই পৃষ্ঠা পুনরায় খোলেন, এই নতুন এন্ট্রিটি একটি নতুন স্লট তৈরি করার সাথে সাথে key পরিবর্তন হবে।

একজন বিকাশকারীর জন্য, key দরকারী কারণ ন্যাভিগেশন API আপনাকে ব্যবহারকারীকে একটি মিল কী সহ একটি এন্ট্রিতে সরাসরি নেভিগেট করতে দেয়। আপনি এটিকে ধরে রাখতে পারবেন, এমনকি অন্যান্য এন্ট্রির রাজ্যেও, যাতে সহজেই পৃষ্ঠাগুলির মধ্যে লাফ দেওয়া যায়।

// 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}) আপনাকে এক জায়গায় এটি পরিচালনা করতে দেবে।

স্টেট বনাম URL প্যারাম

যেহেতু রাজ্য একটি কাঠামোগত বস্তু হতে পারে, এটি আপনার সমস্ত অ্যাপ্লিকেশন অবস্থার জন্য এটি ব্যবহার করতে প্রলুব্ধ করে। যাইহোক, অনেক ক্ষেত্রে ইউআরএলে সেই অবস্থাটি সংরক্ষণ করা ভাল।

ব্যবহারকারী অন্য ব্যবহারকারীর সাথে ইউআরএল শেয়ার করার সময় আপনি যদি রাজ্যটিকে ধরে রাখার আশা করেন, তাহলে সেটিকে ইউআরএলে সংরক্ষণ করুন। অন্যথায়, রাষ্ট্রীয় বস্তুই উত্তম বিকল্প।

সমস্ত এন্ট্রি অ্যাক্সেস

যদিও "বর্তমান এন্ট্রি" সব নয়। এপিআই এন্ট্রিগুলির সম্পূর্ণ তালিকা অ্যাক্সেস করার একটি উপায়ও প্রদান করে যা একজন ব্যবহারকারী তার navigation.entries() কলের মাধ্যমে আপনার সাইট ব্যবহার করার সময় নেভিগেট করেছে, যা এন্ট্রিগুলির একটি স্ন্যাপশট অ্যারে প্রদান করে। এটি ব্যবহার করা যেতে পারে, যেমন, ব্যবহারকারী কীভাবে একটি নির্দিষ্ট পৃষ্ঠায় নেভিগেট করেছে তার উপর ভিত্তি করে একটি ভিন্ন UI দেখাতে, অথবা শুধুমাত্র পূর্ববর্তী URL বা তাদের অবস্থার দিকে ফিরে তাকানোর জন্য। বর্তমান ইতিহাস API এর সাথে এটি অসম্ভব।

আপনি পৃথক NavigationHistoryEntry s-এ একটি "dispose" ইভেন্টের জন্যও শুনতে পারেন, যখন এন্ট্রিটি আর ব্রাউজার ইতিহাসের অংশ থাকে না। এটি সাধারণ পরিচ্ছন্নতার অংশ হিসাবে ঘটতে পারে, কিন্তু নেভিগেট করার সময়ও ঘটতে পারে। উদাহরণস্বরূপ, যদি আপনি 10টি স্থানের পিছনে যান, তারপর সামনের দিকে নেভিগেট করেন, সেই 10টি ইতিহাসের এন্ট্রি নিষ্পত্তি করা হবে।

উদাহরণ

"navigate" ইভেন্টটি উপরে উল্লিখিত সমস্ত ধরণের নেভিগেশনের জন্য কাজ করে। (সকল সম্ভাব্য প্রকারের স্পেকের মধ্যে আসলে একটি দীর্ঘ পরিশিষ্ট আছে।)

যদিও অনেক সাইটের ক্ষেত্রে ব্যবহারকারী একটি <a href="..."> ক্লিক করলে সবচেয়ে সাধারণ ঘটনা হবে, সেখানে দুটি উল্লেখযোগ্য, আরও জটিল নেভিগেশন প্রকার রয়েছে যা কভার করার মতো।

প্রোগ্রাম্যাটিক নেভিগেশন

প্রথমটি হল প্রোগ্রাম্যাটিক নেভিগেশন, যেখানে নেভিগেশন আপনার ক্লায়েন্ট-সাইড কোডের ভিতরে একটি পদ্ধতি কল দ্বারা সৃষ্ট হয়।

নেভিগেশন ঘটানোর জন্য আপনি আপনার কোডের যেকোনো জায়গা থেকে navigation.navigate('/another_page') কল করতে পারেন। এটি "navigate" শ্রোতার নিবন্ধিত কেন্দ্রীভূত ইভেন্ট শ্রোতা দ্বারা পরিচালিত হবে এবং আপনার কেন্দ্রীভূত শ্রোতাকে সিঙ্ক্রোনাসভাবে ডাকা হবে।

এটি পুরানো পদ্ধতিগুলির একটি উন্নত সমষ্টি যেমন location.assign() এবং বন্ধুদের পাশাপাশি History API-এর পদ্ধতি pushState() এবং replaceState()

navigation.navigate() পদ্ধতিটি একটি অবজেক্ট রিটার্ন করে যার মধ্যে দুটি Promise দৃষ্টান্ত রয়েছে { committed, finished } । এটি আমন্ত্রণকারীকে রূপান্তরটি "প্রতিশ্রুতিবদ্ধ" (দৃশ্যমান URL পরিবর্তিত হয়েছে এবং একটি নতুন NavigationHistoryEntry উপলব্ধ) বা "সমাপ্ত" (সকল প্রতিশ্রুতি intercept({ handler }) দ্বারা প্রত্যাবর্তিত হওয়া পর্যন্ত সম্পূর্ণ—বা প্রত্যাখ্যান করা পর্যন্ত অপেক্ষা করতে দেয়। ব্যর্থতা বা অন্য নেভিগেশন দ্বারা preempted হচ্ছে)।

navigate পদ্ধতিতে একটি বিকল্প অবজেক্টও রয়েছে, যেখানে আপনি সেট করতে পারেন:

  • state : NavigationHistoryEntry এন্ট্রিতে .getState() পদ্ধতির মাধ্যমে উপলব্ধ নতুন ইতিহাস এন্ট্রির জন্য রাজ্য।
  • history : যা বর্তমান ইতিহাস এন্ট্রি প্রতিস্থাপন করতে "replace" সেট করা যেতে পারে।
  • info : navigateEvent.info এর মাধ্যমে নেভিগেট ইভেন্টে যাওয়ার জন্য একটি বস্তু।

বিশেষ করে, info উপযোগী হতে পারে, উদাহরণস্বরূপ, একটি নির্দিষ্ট অ্যানিমেশন বোঝাতে যা পরবর্তী পৃষ্ঠাটি দেখায়। (বিকল্পটি হতে পারে একটি গ্লোবাল ভেরিয়েবল সেট করা বা #হ্যাশের অংশ হিসাবে এটিকে অন্তর্ভুক্ত করা। উভয় বিকল্পই কিছুটা বিশ্রী।) উল্লেখযোগ্যভাবে, এই info পুনরায় প্লে করা হবে না যদি কোনো ব্যবহারকারী পরে নেভিগেশন ঘটায়, যেমন, তাদের পিছনে এবং এর মাধ্যমে ফরোয়ার্ড বোতাম। প্রকৃতপক্ষে, এটি সর্বদা সেই ক্ষেত্রে undefined থাকবে।

বাম বা ডান দিক থেকে খোলার ডেমো

navigation আরও অনেকগুলি ন্যাভিগেশন পদ্ধতি রয়েছে, যেগুলি সবগুলি { committed, finished } সম্বলিত একটি বস্তু ফেরত দেয়। আমি ইতিমধ্যে traverseTo() উল্লেখ করেছি (যা একটি key গ্রহণ করে যা ব্যবহারকারীর ইতিহাসে একটি নির্দিষ্ট এন্ট্রি নির্দেশ করে) এবং navigate() । এটিতে back() , forward() এবং reload() অন্তর্ভুক্ত রয়েছে। এই সমস্ত পদ্ধতিগুলি পরিচালনা করা হয় — ঠিক যেমন navigate() —কেন্দ্রীভূত "navigate" ইভেন্ট লিসেনার দ্বারা।

ফর্ম জমা

দ্বিতীয়ত, POST-এর মাধ্যমে HTML <form> জমা দেওয়া হল একটি বিশেষ ধরনের নেভিগেশন, এবং নেভিগেশন API এটিকে আটকাতে পারে। যদিও এটি একটি অতিরিক্ত পেলোড অন্তর্ভুক্ত করে, নেভিগেশন এখনও "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" ইভেন্ট শ্রোতার কেন্দ্রীভূত প্রকৃতি সত্ত্বেও, বর্তমান নেভিগেশন API স্পেসিফিকেশন একটি পৃষ্ঠার প্রথম লোডে "navigate" ট্রিগার করে না। এবং যে সাইটগুলি সমস্ত রাজ্যের জন্য সার্ভার সাইড রেন্ডারিং (SSR) ব্যবহার করে, এটি ঠিক হতে পারে—আপনার সার্ভার সঠিক প্রাথমিক অবস্থা ফিরিয়ে দিতে পারে, যা আপনার ব্যবহারকারীদের কাছে সামগ্রী পাওয়ার দ্রুততম উপায়। কিন্তু যে সাইটগুলি তাদের পৃষ্ঠাগুলি তৈরি করতে ক্লায়েন্ট-সাইড কোড ব্যবহার করে তাদের পৃষ্ঠা শুরু করার জন্য একটি অতিরিক্ত ফাংশন তৈরি করতে হতে পারে।

নেভিগেশন API-এর আরেকটি ইচ্ছাকৃত ডিজাইন পছন্দ হল যে এটি শুধুমাত্র একটি একক ফ্রেমের মধ্যে কাজ করে—অর্থাৎ, শীর্ষ-স্তরের পৃষ্ঠা, অথবা একটি নির্দিষ্ট <iframe> । এটির বেশ কয়েকটি আকর্ষণীয় প্রভাব রয়েছে যা স্পেসে আরও নথিভুক্ত করা হয়েছে , কিন্তু বাস্তবে, বিকাশকারীর বিভ্রান্তি কমিয়ে দেবে। পূর্ববর্তী হিস্ট্রি এপিআই-এ অনেকগুলি বিভ্রান্তিকর এজ কেস রয়েছে, যেমন ফ্রেমের জন্য সমর্থন, এবং পুনর্গঠিত ন্যাভিগেশন এপিআই এই প্রান্তের কেসগুলিকে শুরু থেকেই পরিচালনা করে।

সবশেষে, ব্যবহারকারীর মাধ্যমে নেভিগেট করা এন্ট্রিগুলির তালিকা প্রোগ্রাম্যাটিকভাবে সংশোধন বা পুনর্বিন্যাস করার বিষয়ে এখনও ঐকমত্য নেই। এটি বর্তমানে আলোচনার অধীনে আছে, কিন্তু একটি বিকল্প হতে পারে শুধুমাত্র মুছে ফেলার অনুমতি দেওয়া: হয় ঐতিহাসিক এন্ট্রি বা "সব ভবিষ্যত এন্ট্রি"। পরেরটি অস্থায়ী রাষ্ট্রের অনুমতি দেবে। উদাহরণস্বরূপ, একজন বিকাশকারী হিসাবে, আমি করতে পারি:

  • নতুন URL বা রাজ্যে নেভিগেট করে ব্যবহারকারীকে একটি প্রশ্ন জিজ্ঞাসা করুন
  • ব্যবহারকারীকে তাদের কাজ সম্পূর্ণ করার অনুমতি দিন (বা ফিরে যান)
  • একটি কাজ সমাপ্তির একটি ইতিহাস এন্ট্রি সরান

এটি অস্থায়ী মডেল বা ইন্টারস্টিশিয়ালগুলির জন্য উপযুক্ত হতে পারে: নতুন URL হল এমন কিছু যা থেকে ছেড়ে যাওয়ার জন্য ব্যবহারকারী পিছনের অঙ্গভঙ্গি ব্যবহার করতে পারে, কিন্তু তারা ভুলবশত এটিকে আবার খুলতে ফরোয়ার্ড করতে পারে না (কারণ এন্ট্রিটি সরানো হয়েছে)। বর্তমান ইতিহাস API এর সাথে এটি সম্ভব নয়।

নেভিগেশন API চেষ্টা করুন

নেভিগেশন API Chrome 102-এ পতাকা ছাড়াই উপলব্ধ। আপনি Domenic Denicola দ্বারা একটি ডেমো ব্যবহার করে দেখতে পারেন।

যদিও ক্লাসিক হিস্ট্রি এপিআই সহজবোধ্য দেখায়, এটি খুব ভালভাবে সংজ্ঞায়িত নয় এবং কোণার কেস এবং ব্রাউজার জুড়ে এটি কীভাবে ভিন্নভাবে প্রয়োগ করা হয়েছে তার আশেপাশে প্রচুর সংখ্যক সমস্যা রয়েছে। আমরা আশা করি আপনি নতুন নেভিগেশন এপিআই সম্পর্কে মতামত প্রদান করার কথা বিবেচনা করবেন।

তথ্যসূত্র

স্বীকৃতি

এই পোস্টটি পর্যালোচনা করার জন্য টমাস স্টেইনার , ডোমেনিক ডেনিকোলা এবং নেট চ্যাপিনকে ধন্যবাদ। আনস্প্ল্যাশ থেকে হিরো ইমেজ, জেরেমি জিরো দ্বারা।