vite-pwa / vite-plugin-pwa

Zero-config PWA for Vite

Home Page:https://vite-pwa-org.netlify.app/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Postponing caching of certain pages/files until a certain event occurs

pavlexander opened this issue · comments

I am developing a vuejs3 app with vite and vite-plugin-pwa plugin. The app is login-protected. Therefore whoever opens the webpage - should not have to load all of the pages of the application right away. Only authenticated users should have all of the app components pre-cached.

By default - vite-plugin-pwa caches all of the pages, including the JS assets from assets folder, where lazy-loaded views reside.

Here is the example router config I am using:

import { createRouter, createWebHistory } from "vue-router";
import Homefrom "@/views/Home.vue";
import PublicPage from "@/views/PublicPage.vue";

const routes = [
  {
    path: "/",
    name: "home",
    component: Home,
  },
  {
    path: "/publicPage",
    name: "publicPage",
    component: PublicPage,
  },
  {
    path: "/securePage",
    name: "securePage",
    component: () =>
      import("@/views/SecurePage.vue"),
  },
];

In the example above the views Home and PublicPage are accessible by any user. On the other hand the SecurePage is only accessible by authenticated users (this logic is in other place so you have to believe me :) ).. The npm run build command outputs following files in assets folder:

  • index.b3c1a1d3.js
  • SecurePage.6380b994.js

image

I would like the PWA to cache SecurePage at the later time. So, my question is - how should I approach this problem?

  1. I think it should be possible to use the message event to inform the service worker that some additional pages need to be cached. Is this correct?
  2. How do I disable the default behavior of caching all the js files under assets folder? ( I couldn't not find any documentation about it)
  3. How to make sure that later-cached pages are updated/kept up to date with the rest of the files when there is a new version?

In general it would be nice to see some code samples, if not of the whole solution, then at least of the methods, events that need to be modified or invoked :)

sw.js

import { precacheAndRoute } from "workbox-precaching";

precacheAndRoute(self.__WB_MANIFEST);

vite.config.js

import path from "path";

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import { VitePWA } from "vite-plugin-pwa";

export default defineConfig({
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src"),
    },
  },
  alias: {
    "/@": path.resolve(__dirname, "./src"),
  },
  plugins: [
    vue(),
    VitePWA({
      base: "/",
      srcDir: "src",
      includeAssets: [
        "favicon.ico",
        "robots.txt",
        "apple-touch-icon.png",
      ],
      strategies: "injectManifest",
      manifest: {
        name: "Name of your app",
        short_name: "Short name of your app",
        description: "Description of your app",
        theme_color: "#ffffff",
        icons: [
          {
            src: "./img/icons/android-chrome-192x192.png",
            sizes: "192x192",
            type: "image/png",
          },
          {
            src: "./img/icons/android-chrome-512x512.png",
            sizes: "512x512",
            type: "image/png",
          },
          {
            src: "./img/icons/android-chrome-maskable-192x192.png",
            sizes: "512x512",
            type: "image/png",
            purpose: "any maskable",
          },
          {
            src: "./img/icons/android-chrome-maskable-512x512.png",
            sizes: "512x512",
            type: "image/png",
            purpose: "maskable",
          },
        ],
      },
    }),
  ],
});

package.json

{
  "name": "vite-pwa-1",
  "private": true,
  "version": "0.0.0",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "localforage": "^1.10.0",
    "vue": "^3.2.25",
    "vue-router": "^4.0.14"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^2.3.1",
    "vite": "^2.9.5",
    "vite-plugin-pwa": "^0.11.13"
  }
}

@pavlexander you can exclude secured pages from the sw precache manifest (will not work on offline, but also you cannot login into the app) or you can protect the data on that pages (loading data before routing to the page and/or prevent that transition: you will need to protect on the server, no matter if the page can be fetched or not).

You can also build a custom sw with custom routing, but yo will need to check the workbox docs.

I have all my apps with all pages with public access, anyone can request secured pages: I use an interceptor on the client checking the login state to prevent routing (redirect to home) and on server checking also the user is logged when requesting secured data.

@userquin I should have probably mentioned that this is not done for security reasons. There is no such thing as security for the frontend :D The backend server is protected against the write attempts of unauthenticated users just fine.

The reason I was thinking about is the SEO optimization. There are 21 pages on production environment that are secured by authentication. Total size of these files are 512kb (for JS) at this moment (+15kb CSS). So, in theory, I could save around 0.5mb of data for every single page viewing requests for every user. As well as for google bot and other crawlers, hence resulting in higher site rankings.

You can also build a custom sw with custom routing, but yo will need to check the workbox docs.

For this to happen - do I have to basically ditch the vite-plugin-pwa plugin? I mean if I switch to barebone workbox solution then the plugin is not required, right? I am wondering if there are additional quality of life improvements done in this plugin, that I should be concerned about, before switching?

I have all my apps with all pages with public access, anyone can request secured pages: I use an interceptor on the client checking the login state to prevent routing (redirect to home) and on server checking also the user is logged when requesting secured data.

Yes this sounds like a good option when the app/page size is irrelevant :)

@pavlexander you can exclude secured pages from the sw precache manifest (will not work on offline, but also you cannot login into the app) or you can protect the data on that pages (loading data before routing to the page and/or prevent that transition: you will need to protect on the server, no matter if the page can be fetched or not).

currently there is a beforeRoute guard that redirects the users to the home page if no login session is detected. Hence none of the protected resources are loaded under any circumstances (non-pwa version of the site). Out of curiosity - could you link the documentation page where the "you can exclude secured pages from the sw precache manifest" is explained? I could not find anything on this topic. As I understood all the files that are built are auto-included.

image

For testing purposes I have set the following plugin config:

    VitePWA({
    //...
      workbox: {
        globPatterns: ["**/*.{js,css}"],
      },
    //...
    }),

Default value was globPatterns: ["**/*.{js,css,html}"],. (according to https://developer.chrome.com/docs/workbox/reference/workbox-build/#type-GlobPartial)

After the build - I can still find the html file in a precache list:

{ revision: "be53e918256068bde0ecb0f935d98b65", url: "index.html" },

So it seems like property override has no effect. Am I doing something wrong?

Edit: neither did the globIgnores: ["**/SecurePage*"], work..

{ revision: null, url: "assets/SecurePage.86b6aeec.js" },

@pavlexander SEO here is not involved, the sw will precache all the manifest entries on a background thread, it will take more time to be ready/update when the app is huge, but should not be a problem: lighthouse can be wrong including that resources on the results.

EDIT: as an example you can take a look at vueuse docs where you have a ton of pages on the sw (also using vite-plugin-pwa). You can open the dev tools before entering on the page, you will see the app is working while the background thread is precaching (downloading) all the assets, it will take time, just wait to the sw activated and ready (green status on application > Service Workers on dev tools)

For the custom sw, you can write it using TypeScript with static imports using injectManifest strategy on the pwa plugin configuration instead using old importScript, you only need to write the sw: check the examples/vue-router/src/claims-sw.ts, you can also read the Workbox > injectManifest entry on the docs.

To exclude some pages from the sw you need to use workbox.navigateFallbackDenylist entry, it is an array of regex, for example:

workbox: {
  navigateFallbackDenylist: [/^\/securePage/],
  /* more options */
}

EDIT: about excluding index.html do not this since it is the fallback and must be included on the sw manifest, the browser will complain when installing the sw if missing.

The reason I was thinking about is the SEO optimization. There are 21 pages on production environment that are secured by authentication. Total size of these files are 512kb (for JS) at this moment (+15kb CSS). So, in theory, I could save around 0.5mb of data for every single page viewing requests for every user. As well as for google bot and other crawlers, hence resulting in higher site rankings.

The sw will prefetch all assets only once or when updated found, if you navigate once sw activated the browser will never request the page to the server, the sw will respond with the cache (stale while revalidate): only a hard refresh from the browser will reload the current server page and its assets, but the sw will never download again the sw precache, since it will be updated (of course if you have a new version on the server, all new assets will be downloaded in the background thread).

Just try with the docs of this plugin, or with vueuse docs: once sw activated, just clear the network tab on dev tools, press F5 or Crtl/Command + F5 to do a hard refresh.

EDIT: as an example you can take a look at vueuse docs where you have a ton of pages on the sw (also using vite-plugin-pwa). You can open the dev tools before entering on the page, you will see the app is working while the background thread is precaching (downloading) all the assets, it will take time, just wait to the sw activated and ready (green status on application > Service Workers on dev tools)

Great example! It really is loading resources on the background! I thought it's all done on initial page load synchronously. In this case SEO should not be a concern. A bunch of unnecessary files downloaded? Maybe. I will have to do the re-prioritization :)

For the custom sw, you can write it using TypeScript with static imports using injectManifest strategy on the pwa plugin configuration instead using old importScript, you only need to write the sw: check the examples/vue-router/src/claims-sw.ts, you can also read the Workbox > injectManifest entry on the docs.

To exclude some pages from the sw you need to use workbox.navigateFallbackDenylist entry, it is an array of regex, for example:

workbox: {
  navigateFallbackDenylist: [/^\/securePage/],
  /* more options */
}

EDIT: about excluding index.html do not this since it is the fallback and must be included on the sw manifest, the browser will complain when installing the sw if missing.

It was not my intent to exclude the index.html. I used it to check if the globPatterns tag is working at all or not. Seems like not. Could you please elaborate on workbox configuration?

I've tried following your advice with following configuration:

  workbox: {
    navigateFallbackDenylist: [
      "**/SecurePage*",
      "/SecurePage",
      "/SecurePage",
      /^\/securePage/,
      /^\/SecurePage/,
      "^[^ ]*SecurePage",
      /[^ ]*SecurePage/,
      /[^ ]*SecurePage[^ ]*/,
    ],
    globIgnores: [
      "**/SecurePage*",
      "/SecurePage",
      "/SecurePage",
      /^\/securePage/,
      /^\/SecurePage/,
      "^[^ ]*SecurePage",
      /[^ ]*SecurePage/,
      /[^ ]*SecurePage[^ ]*/,
    ],
  },

Upon opening the sw.js in distribution folder I can see that the SecurePage is still found among the precache manifest list:

image

We can further verify it by cleaning old caches, unregistering the service, refreshing the page and forcing the service to create new caches:

image

image

So for some reason this navigateFallbackDenylist tag has no effect. Similarly, I tried using the globPatterns and globIgnores but the values that I set are ignored. Is this a bug or did I screw the configuration up somehow?

Then you can exclude all from globIgnored option on worbox, but keep also the deny list option.

Upps, you have it configured, it should be a glob expression. If not working you can use manifestTransforms callback option removing the entries yourself, check pwa-configuration.js on the sveltekit example in this repo.

EDIT: https://github.com/antfu/vite-plugin-pwa/blob/main/examples/sveltekit-pwa/pwa-configuration.js#L52

You should filter the entries by url, ignore the logic for sveltekit.

I ended up configuring my own script to transform the sw.js file with pure workbox solution, no addons. This way I get a better control of what's happening.

@pavlexander can you share your SW configuration to see how you did it without vite pwa plugin ?