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

Initial flashing of index route on page load (when another route is requested) #159

Closed
kemalcany opened this issue Jul 26, 2021 · 13 comments
Assignees

Comments

@kemalcany
Copy link

Hi!

When you open the app with a different route than index (as its seen in /about below), there is a split second of index route flashing. Happens both on static and dynamic routes.

You can observe this behaviour (although it's very quick) on the official example behaviour as well:

https://vitesse.netlify.app/about
or
https://vitesse.netlify.app/hi/something

Is this the expected behaviour? Is there a way to avoid this, even if that means a slower initial page load?

Thank you!

@antfu
Copy link
Member

antfu commented Jul 26, 2021

SSG seem to broken, investigating.

@antfu
Copy link
Member

antfu commented Jul 27, 2021

Looks like it's mainly because PWA is serving the wrong file, /cc @userquin do you have any idea on how could we make PWA aware of the url prettify?

@userquin userquin self-assigned this Jul 27, 2021
@userquin
Copy link
Member

userquin commented Jul 27, 2021

@antfu The problem is that routes are not included in the sw manifest, and so the router will get the index.html response, and then redirected from router logic: the flash occurs while resolving requested page assets (router page transition from index to requested).

If you look at the sw.js file from netlify you can see home, 404 and about routes are missing from the precache and so the problem isn't from url prettify, first we need to include them on sw precache and then make them url pretiffy aware.

I'm looking for a way to map routers to logical names and include them into the sw manifest: but I think it will be hard.

In my projects I include them manually.

@userquin
Copy link
Member

userquin commented Jul 27, 2021

@antfu I have it working, we need to do some changes to track routes using extendRoute from vite-plugin-pages, you can see it on my branch here.

EDIT: my branch is to show you how to solve the problem, it is a trick to make it working, I would like to modify vite-ssg cli to make the solution more robust since vite-ssg has the info to do it well. See my last comment entry using the second approach, just provide the array to vite-ssg (will calculate url and revision for each route) and to pwa plugin.

The changes are on vite.config.ts file on vite-plugin-pages plugin adding the corresponding workbox ManifestEntry.

The problem is that each time the project is built the sw is updated: since the sw is generated on client generation from vite-ssg we need to include extendRoute on vite-plugin-pages to collect all routes, excluding / and dynamic routes, but we need to calculate the revision entry for additionalManifestEntries workbox option, but the corresponding html is generated after client generation and so we haven't it, we cannot use the generate html to calculate the revision, we only can do some random and use it hash as the revision.

On my projects I build the revision this way.

imagen

imagen

imagen

imagen

@userquin
Copy link
Member

userquin commented Jul 27, 2021

@antfu maybe we can change the order on vite-ssg, generating client last, collecting routes and once generated then we can calculate the real revision (if the html is not changed, then the sw will not change if none of assets nor html routes change). The problem is still on user land, since we need to calculate the revision.

I don't know if we can access to vite configuration (from vite-ssg cli) and then change the PWA plugin configuration from vite-ssg (we'll need to detect it). The real problem is that the mapping is done by vite-ssg but the routes comes from vite-plugin-pages and we need both, the url for each route and its html on dist folder.

For example, generating client last on vite-ssg cli we can modify its onFinish passing routes and revision map, similar how I do on my branch, this way we'll have that info and ppl can use it (maybe we can add an option to ssg option to enable it or maybe we can add another ssg option for that purpose):

export interface ViteSSGOptions {
  ...
  generateRouteAndRevision?: boolean // <== if generating for passing them to onFinished
  onFinished?: (routesUrlAndRevisions: Map<string, string>) => void // <== can be a Record<string, string>
}

In this scenario we'll have this:

// vite.config.ts
const pwaRouterManifestEntries: any[] = []

export default defineConfig({
  ...
  ...
  plugins: [
    ...
    ...
    VitePWA({
      ...
      workbox: {
        additionalManifestEntries: pwaRouterManifestEntries,
      },
      // or
      injectManifest: {
        additionalManifestEntries: pwaRouterManifestEntries,
      },
    }),
    ...
  ],
  ...
  ...
  ssgOptions: {
    script: 'async',
    formatting: 'minify',
    generateRouteAndRevision: true,
    onFinished(routesUrlAndRevisions) {
      // for map
      routesUrlAndRevisions.forEach((revision, url) => pwaRouterManifestEntries.push({ url, revision }))
      // for record
      Object.keys(routesUrlAndRevisions).forEach(url => pwaRouterManifestEntries.push({ url, revision: routesUrlAndRevisions[url] })
    }
  },
  ...
  ...
})

Second option is to add an explicit option such as pwaRouterManifestEntries; this way just configuring pwaRouterManifestEntries on vite.config.ts file and configure it on workbox or injectManifest pwa plugin option and we are done:

export interface ViteSSGOptions {
  ...
  onFinished?: () => void
  pwaRouterManifestEntries?: any[]
}

In this last scenario we can have this:

// vite.config.ts
const pwaRouterManifestEntries: any[] = []

export default defineConfig({
  ...
  ...
  plugins: [
    ...
    ...
    VitePWA({
      ...
      workbox: {
        additionalManifestEntries: pwaRouterManifestEntries,
      },
      // or
      injectManifest: {
        additionalManifestEntries: pwaRouterManifestEntries,
      },
    }),
    ...
  ],
  ...
  ...
  ssgOptions: {
    script: 'async',
    formatting: 'minify',
    pwaRouterManifestEntries,
  },
  ...
  ...
})

In both cases, we need to mofify the logic on vite-ssg cli to calculate the manifest entry for each route when needed, but the logic is simple just a few minutes and we're done.

I like the second approach, what do you think?

@antfu
Copy link
Member

antfu commented Jul 28, 2021

@userquin thanks for the detailed explanation. Let me take some time to think about how to have them work together properly.

@userquin
Copy link
Member

@antfu we cannot move client generation to last step since we need the ssr manifest: it is also generated on client build step.

@userquin
Copy link
Member

userquin commented Jul 29, 2021

@antfu I have an entry on vite about handling modulepreload here, it is also related to this problem.

I also mention this problem on i18n router discussion entry on vite-ssg, since we have a spa we need to generate also client manifest to extract non index page dependencies to be injected on generated html page.

For dynamic page entries I think we need to find a way on client side to make page transition smooth while resolving initial router transition.

@userquin
Copy link
Member

The link for vite-ssg modulepreload can be found here.

@antfu antfu closed this as completed in 3112490 Jul 31, 2021
@antfu
Copy link
Member

antfu commented Jul 31, 2021

Fixed with 3112490 by upgrading:

  • vite-plugin-pwa@^0.9.3
  • vite-ssg@^0.13.0

Thanks again @userquin for the detailed explanation and working example, even though the approaches are a bit different, your solution is definitely helpful :)

@kemalcany
Copy link
Author

@antfu @userquin Thank you very much!!!

@kemalcany
Copy link
Author

Hi!

Looks like this issue is back. Please observe the initial load for this page:

https://vitesse.netlify.app/hi/something

You will see the index page flashing for a split second

@userquin
Copy link
Member

It is not back, this page is dynamic and so excluded by default from ssg, I wrote about it on discussions a few months ago.

I am checking how to solve this problem.

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

3 participants