Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to use navigationType == 'back_forward' to determine whether the page is loaded using bfcache. #393

Closed
zengzuo613 opened this issue Oct 12, 2023 · 7 comments

Comments

@zengzuo613
Copy link

I have read your section on bfcache, in the "Measuring your bfcache hit ratio" section, you introduced that the navigationType value can also be used to determine whether the page uses bfcache. In my test code, after loading the page with bfcache, the navigationType value is the same as when the page was first loaded, it does not change to back_forward.

run

The page can utilize bfcache.

image

code

    window.addEventListener("pageshow", function (event) {
        if (event.persisted) {
            console.log('This page *might* be entering the bfcache.');
        } else {
            console.log('This page will unload normally and be discarded.');
        }
        console.log('navigationType:' + performance.getEntriesByType('navigation')[0].type);
    });

The code runs in Chrome 117.

console result

image

question

The navigation type is always reload, because the PerfromaceNavigationTiming value is from the previous page. How can I get the navigation type to be back_forward? In what scenarios would the navigation type value be back_forward?

@tunetheweb
Copy link
Member

The navigation type from performance.getEntriesByType("navigation")[0].type; will always be the original navigation type depending how the page was loaded. The fact this shows reload shows that this page was originally loaded with a reload. When the bfcache is used the page is restored from memory and the original type is still in memory.

Additionally this code is incorrect:

    window.addEventListener("pageshow", function (event) {
        if (event.persisted) {
            console.log('This page *might* be entering the bfcache.');
        } else {
            console.log('This page will unload normally and be discarded.');
        }
        console.log('navigationType:' + performance.getEntriesByType('navigation')[0].type);
    });

event.persisted is set when the page is restored from the bfcache, not when it enters it. So, this should say:

    window.addEventListener("pageshow", function (event) {
        if (event.persisted) {
            console.log('This page has been restored from the bfcache.');
        } else {
            console.log('This page was loaded normally and not from the bfcache.');
        }
        console.log('Original navigationType:' + performance.getEntriesByType('navigation')[0].type);
    });

Anyway, to test this go to any site (let's use https://example.com as an example) and do the following:

  • Visit https://example.com
  • Open the console
  • Reload the page
  • Confirm the performance.getEntriesByType("navigation")[0].type; is set to reload
  • Paste the above code into the console
  • Click on the "More information..." link to navigation away from https://example.com
  • Click back
  • You should see "This page has been restored from the bfcache." and "Original navigationType:reload" in the console.

If you install the Web Vitals Extension and turn on console logging in the extension options then you should see the following:

Screenshot of DevTools console

This shows the console logging from the above snippet, and also each Web Vital metric and you can see this JavaScript library actually overrides the navigationType to be back-forward-cache in this case. However, the default from performance.getEntriesByType('navigation')[0].type is still the original reload one.

So if you use the web-vitals library, then this takes care of all this logic for you. And you can just log the navigationType and compare how many back-forward-cache entries you got to how many back-forward entries and calculate your hit rate.

In the section of the article you linked we're saying to do the following:

window.addEventListener('pageshow', (event) => {
  // You can measure bfcache hit rate by tracking all bfcache restores and
  // other back/forward navigations via a separate event.
  const navigationType = performance.getEntriesByType('navigation')[0].type;
  if (event.persisted || navigationType == 'back_forward' ) {
    gtag('event', 'back_forward_navigation', {
      'isBFCache': event.persisted,
    });
  }
});

So if the page has event.persisted set then we know it's a bfcache hit, regardless of the navigationType (hence the ||).

Similarly if the page has navigationType == 'back_forward' then we know it's a back/forward navigation (which may or may not have been restored from the bfcache).

If it's anything else (e.g. a reload navigation type which was not restored from the bfcache) then it's not a back/forward navigation and so we're excluding these navigations. It would be good to log them for other analysis but for calculating the bfcache ratio they are not necessary.

Let me know if that all makes sense or if you have any further questions.

@tunetheweb
Copy link
Member

Ah I see where you got the 'This page *might* be entering the bfcache.' from that article. That is based on the pagehide event not the pageshow one you had in your code. As the article notes though:

However, if the persisted property is true, it doesn't guarantee that a page will be cached. It means that the browser intends to cache the page, but there may be factors that make it impossible to cache.

So definitely recommend instead to measure using the persisted value of the pageshow event, which is when the bfcache was actually used.

@zengzuo613
Copy link
Author

Thank you for your patient explanations. Furthermore, am I right to understand that it's impossible to determine if a page is loaded from bfcache through performance.getEntriesByType('navigation')[0].type? Hence, web-vitals uses getBFCacheRestoreTime() >= 0 to make this judgement.

export const initMetric = (name: Metric['name'], value?: number): Metric => {
  const navEntry = getNavigationEntry();
  let navigationType: Metric['navigationType'] = 'navigate';

  if (getBFCacheRestoreTime() >= 0) {
    navigationType = 'back-forward-cache';
  } else if (navEntry) {
    if (document.prerendering || getActivationStart() > 0) {
      navigationType = 'prerender';
    } else if (document.wasDiscarded) {
      navigationType = 'restore';
    } else {
      navigationType = navEntry.type.replace(
        /_/g,
        '-'
      ) as Metric['navigationType'];
    }
  }

  return {
    name,
    value: typeof value === 'undefined' ? -1 : value,
    rating: 'good', // Will be updated if the value changes.
    delta: 0,
    entries: [],
    id: generateUniqueID(),
    navigationType,
  };
};

@tunetheweb
Copy link
Member

tunetheweb commented Oct 13, 2023

That's correct. As a bfcache restore is not a navigation, it doesn't have it's own navigation entry in the performance timeline and it just restores the old one.

This is something I personally think is confusing btw! Especially as it requires understanding all those types (bfcache, prerender, restore), and how they differ so did raise an issue to discuss changing this: w3c/navigation-timing#184. But there is concern about updating an old entry.

@zengzuo613
Copy link
Author

Thank you, I have another question to ask. For a page loaded from bfcache, if the pageshow event is triggered, can we consider the page to be fully loaded? Similar to the onload event of a regular page?

The MDN documentation does not clarify my doubts.

@tunetheweb
Copy link
Member

Well that's an interesting question!

And in fact I'm in the middle of updating our guidance on that. At the moment we suggest measuring all navigations on pageshow event. But this can actually fire quite late for the initial loads and the user may have navigated away before that even happens. So I'm in the middle of updating our docs to instead suggest this:

// Send a navigation_type when the page is first loaded.
gtag('event', 'page_view', {
   'navigation_type': performance.getEntriesByType('navigation')[0].activationStart;
});

window.addEventListener('pageshow', (event) => {
  if (event.persisted) {
    // Send another pageview if the page is restored from bfcache.
    gtag('event', 'page_view', {
      'navigation_type': 'back_forward_cache';
    });
  }
});

So the initial page view is logged ASAP and pageshow is only used for bfcache navigations. This is similar to the prvious snippet in the docs.

So what happens if the page is load, but then navigated away from before it fully loads, and then you go back to that page and use the bfcache? I've not tested that to be honest.

@zengzuo613
Copy link
Author

tks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants