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

Viewport prefetching #3633

Closed
bluwy opened this issue Jan 30, 2022 · 4 comments
Closed

Viewport prefetching #3633

bluwy opened this issue Jan 30, 2022 · 4 comments
Labels
feature request New feature or request p2-nice-to-have SvelteKit cannot be used by a small number of people, quality of life improvements, etc. prefetch
Milestone

Comments

@bluwy
Copy link
Member

bluwy commented Jan 30, 2022

Describe the problem

Currently sveltekit:prefetch works by requesting data on hover or before click only. In some cases, it may be desirable to prefetch immediately when the link enters the viewport, similar to how nextjs works today.

#3041 also highlighted some points there.

Describe the proposed solution

Provide a way to define the prefetching strategy, either by viewport or hover. This can be achieved by passing a value to the attribute, e.g. sveltekit:prefetch="<strategy>". By default with no value, it would fallback to the hover strategy.

For viewport prefetching, IntersectionObservers can't be used as anchor tags change often and to detect that MutationObservers are needed, but that imposes a lot of potential overhead. An alternative is to query select anchor tags and prefetch them in short intervals. (Ideas welcomed)

To detect when to invalidate the data, maxage can be used. To avoid discouraging short maxage (which indirectly cause repeated prefetching), a syntax like sveltekit:prefetch="viewport:1000" could be supported to denote "invalidate after 1s". Or a config option can be used to set a minimum prefetch interval (similar #790)

Alternatives considered

Refer users to implement their own viewport prefetch logic using actions. Caveat is that links in contents from CDN can't take advantage of the declarative prefetching syntax.

Importance

nice to have

Additional Information

  1. Rich noted that prefetch invalidation may not always be wasteful as the modules requested are usually reusable, only the data is stale.
  2. Adding viewport prefetch implementation may require some infrastructure refactoring.
  3. I might've forgotten some points from the last maintainer's meeting.
@bluwy bluwy added the feature request New feature or request label Jan 30, 2022
@madeleineostoja
Copy link

madeleineostoja commented Feb 11, 2022

Just wanted to chime in again that adding this kind of config to every anchor element in an app is a lot of user overhead in terms of DX which is also introduces more surface for user-error. IMO if this was implemented it would be an even stronger argument for adding the ability to prefetch all relative links by default at the same time, with this stuff configured globally in the same place. A prefetch option under kit in svete.config.js feels like a no-brainer, with per-link overrides possible.

@NagabhushanS
Copy link

NagabhushanS commented Apr 24, 2022

I wrote my own implementation of a Prefetch link whenever the link is in viewport. I additionally added a prop which if set to false will only prefetch the page and not run the load method (as in NextJS where load is not run).

For checking whether the link is in viewport, I think its better to use Intersection Observer API

<script lang="ts">
  import { goto, prefetch, prefetchRoutes } from "$app/navigation";

  export let to: string;
  export let preload: boolean = true;

  let fetched = false;

  const isInViewPort = (element: any) => {
    const rect = element.getBoundingClientRect();
    return (
      rect.top >= 0 &&
      rect.left >= 0 &&
      rect.bottom <=
        (window.innerHeight + rect.height ||
          document.documentElement.clientHeight) &&
      rect.right <= (window.innerWidth || document.documentElement.clientWidth)
    );
  };

  const viewPortAction = (element: any) => {
    const interval = setInterval(() => {
      if (!fetched && isInViewPort(element)) {
        //prefetch the page
        if (preload) {
          prefetch(to).finally(() => {
            fetched = true;
            clearInterval(interval);
          });
        } else {
          prefetchRoutes([to]).finally(() => {
            fetched = true;
            clearInterval(interval);
          });
        }
      }
    }, 200); // check if link is in viewport every 200 millis

    return {
      destroy: () => {
        clearInterval(interval);
      },
    };
  };

  const handleLinkClick = () => {
    goto(to);
  };
</script>

{#if preload}
  <a href={to} use:viewPortAction on:click|preventDefault={handleLinkClick}>
    <slot />
  </a>
{:else}
  <a href={to} use:viewPortAction on:click|preventDefault={handleLinkClick}
    ><slot /></a
  >
{/if}

@Rich-Harris Rich-Harris added this to the 1.0 milestone May 16, 2022
@Rich-Harris
Copy link
Member

Another strategy might be 'focus'. If I navigate to a link with the tab key, maybe that link should start prefetching? Then again, doing so would typically mean prefetching all the links you tab through until you get to the one you want, so maybe not. Unclear. At the very least we should prefetch on keydown for focused links.

@benmccann benmccann added the p2-nice-to-have SvelteKit cannot be used by a small number of people, quality of life improvements, etc. label Jul 29, 2022
@Rich-Harris Rich-Harris modified the milestones: 1.0, whenever Jul 30, 2022
@Rich-Harris
Copy link
Member

this can be closed now that we have data-sveltekit-preload-code="viewport"

https://kit.svelte.dev/docs/link-options#data-sveltekit-preload-code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature request New feature or request p2-nice-to-have SvelteKit cannot be used by a small number of people, quality of life improvements, etc. prefetch
Projects
None yet
Development

No branches or pull requests

5 participants