首页 > 解决方案 > I want my CRA PWA to fetch from cache only if there is no internet. Basically I want it to be a Network First approach

问题描述

Attaching my service worker files and package.json. I think I'm missing a step here, otherwise this should have worked. I don't know what else to add here. This should have worked. I even keep clearing all data from my browser, so that the service workers keep updating. But when I open the website, it still shows me the last version of the website, not the latest version.

sw-custom.js

if (typeof importScripts === "function") {
  importScripts(
    "https://storage.googleapis.com/workbox-cdn/releases/5.0.0/workbox-sw.js"
  );
  /* global workbox */
  if (workbox) {
    console.log("Workbox is loaded");
    workbox.core.skipWaiting();

    /* injection point for manifest files.  */
    workbox.precaching.precacheAndRoute([]);
    /* custom cache rules */
    workbox.routing.registerRoute(
      new workbox.routing.NavigationRoute(new workbox.strategies.NetworkOnly())
    );
  } else {
    console.log("Workbox could not be loaded. No Offline support");
  }
}

sw-build.js

const workboxBuild = require("workbox-build");

const buildSW = () => {
  // The build is expected to fail if the
  // sw install rules couldn't be generated.
  // Add a catch block to handle this scenario.
  return workboxBuild
    .injectManifest({
      swSrc: "src/sw-custom.js", // custom sw rule

      swDest: "build/sw.js", // sw output file (auto-generated

      globDirectory: "build",

      globPatterns: ["**/*.{js,css,html,png,svg}"],

      maximumFileSizeToCacheInBytes: 10 * 1024 * 1024,
    })
    .then(({ count, size, warnings }) => {
      warnings.forEach(console.warn);
      console.info(`${count} files will be precached,
                  totaling ${size / (1024 * 1024)} MBs.`);
    });
};

buildSW();

serviceWorker.js

// This optional code is used to register a service worker.
// register() is not called by default.

import { ACTIONS } from "./utils/actions";
import { store } from "./utils/configureStore";

// This lets the app load faster on subsequent visits in production, and gives
// it offline capabilities. However, it also means that developers (and users)
// will only see deployed updates on subsequent visits to a page, after all the
// existing tabs open on the page have been closed, since previously cached
// resources are updated in the background.

// To learn more about the benefits of this model and instructions on how to
// opt-in, read -PWA

const isLocalhost = Boolean(
  window.location.hostname === "localhost" ||
    // [::1] is the IPv6 localhost address.
    window.location.hostname === "[::1]" ||
    // 127.0.0.1/8 is considered localhost for IPv4.
    window.location.hostname.match(
      /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
    )
);

export function register(config) {
  if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) {
    // The URL constructor is available in all browsers that support SW.
    const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
    if (publicUrl.origin !== window.location.origin) {
      // Our service worker won't work if PUBLIC_URL is on a different origin
      // from what our page is served on. This might happen if a CDN is used to
      // serve assets; see https://github.com/facebook/create-react-app/issues/2374
      return;
    }
    window.addEventListener("load", () => {
      // const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;

      const swUrl = `${process.env.PUBLIC_URL}/sw.js`;

      if (isLocalhost) {
        // This is running on localhost. Let's check if a service worker still exists or not.
        checkValidServiceWorker(swUrl, config);

        // Add some additional logging to localhost, pointing developers to the
        // service worker/PWA documentation.
        navigator.serviceWorker.ready.then(() => {
          console.log(
            "This web app is being served cache-first by a service " +
              "worker. To learn more, visit -PWA"
          );
        });
      } else {
        // Is not localhost. Just register service worker
        registerValidSW(swUrl, config);
      }
    });
  }
}

function registerValidSW(swUrl, config) {
  navigator.serviceWorker
    .register(swUrl)
    .then((registration) => {
      registration.onupdatefound = () => {
        const installingWorker = registration.installing;
        if (installingWorker == null) {
          return;
        }
        installingWorker.onstatechange = () => {
          if (installingWorker.state === "installed") {
            if (navigator.serviceWorker.controller) {
              // At this point, the updated precached content has been fetched,
              // but the previous service worker will still serve the older
              // content until all client tabs are closed.
              console.log(
                "New content is available and will be used when all " +
                  "tabs for this page are closed. See -PWA."
              );

              // Execute callback
              if (config && config.onUpdate) {
                config.onUpdate(registration);
              }
            } else {
              // At this point, everything has been precached.
              // It's the perfect time to display a
              // "Content is cached for offline use." message.
              console.log("Content is cached for offline use.");

              // Execute callback
              if (config && config.onSuccess) {
                config.onSuccess(registration);
              }
            }
          }
        };
      };
    })
    .catch((error) => {
      console.error("Error during service worker registration:", error);
    });
}

function checkValidServiceWorker(swUrl, config) {
  // Check if the service worker can be found. If it can't reload the page.
  fetch(swUrl)
    .then((response) => {
      // Ensure service worker exists, and that we really are getting a JS file.
      const contentType = response.headers.get("content-type");
      if (
        response.status === 404 ||
        (contentType != null && contentType.indexOf("javascript") === -1)
      ) {
        // No service worker found. Probably a different app. Reload the page.
        navigator.serviceWorker.ready.then((registration) => {
          registration.unregister().then(() => {
            window.location.reload();
          });
        });
      } else {
        // Service worker found. Proceed as normal.
        registerValidSW(swUrl, config);
      }
    })
    .catch(() => {
      console.log(
        "No internet connection found. App is running in offline mode."
      );
    });
}

export function unregister() {
  if ("serviceWorker" in navigator) {
    navigator.serviceWorker.ready.then((registration) => {
      registration.unregister();
    });
  }
}
register();

package.json

{
  "name": "admin",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@date-io/date-fns": "^1.3.9",
    "@date-io/moment": "^2.4.0",
    "@devexpress/dx-react-chart": "^2.6.2",
    "@devexpress/dx-react-chart-bootstrap4": "^2.6.2",
    "@devexpress/dx-react-chart-material-ui": "^2.6.2",
    "@devexpress/dx-react-core": "^2.6.2",
    "@devexpress/dx-react-scheduler": "^2.5.0",
    "@devexpress/dx-react-scheduler-material-ui": "^2.5.0",
    "@material-ui/core": "^4.3.1",
    "@material-ui/icons": "^4.2.1",
    "@material-ui/lab": "^4.0.0-alpha.24",
    "@material-ui/pickers": "^3.2.4",
    "@material-ui/styles": "^4.3.0",
    "@uppy/core": "^1.2.0",
    "@uppy/react": "^1.2.0",
    "antd": "^3.22.2",
    "antd-mobile": "^2.3.1",
    "axios": "^0.19.2",
    "braft-editor": "^2.3.9",
    "clsx": "^1.1.0",
    "connected-react-router": "^6.5.2",
    "date-fns": "^2.0.0-beta.5",
    "firebase": "^6.3.4",
    "localforage": "^1.7.3",
    "lodash": "^4.17.15",
    "material-table": "^1.57.2",
    "moment": "^2.24.0",
    "notistack": "^0.8.9",
    "qs": "^6.9.3",
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "react-firebaseui": "^4.0.0",
    "react-geocode": "^0.2.1",
    "react-geosuggest": "^2.13.0",
    "react-google-maps": "^9.4.5",
    "react-helmet": "^5.2.1",
    "react-mentions": "^3.3.1",
    "react-redux": "^7.1.0",
    "react-router-dom": "^5.0.1",
    "react-scripts": "3.0.1",
    "react-virtualized": "^9.21.2",
    "redux": "^4.0.4",
    "redux-persist": "^5.10.0",
    "redux-thunk": "^2.3.0",
    "styled-components": "^4.3.2",
    "tabletop": "^1.6.2",
    "uppy": "^1.3.0",
    "uuid": "^3.3.2"
  },
  "devDependencies": {
    "babel-plugin-import": "^1.12.0",
    "customize-cra": "^0.5.0",
    "husky": "^3.0.2",
    "jest-dom": "^4.0.0",
    "jest-enzyme": "^7.0.2",
    "less": "^3.10.2",
    "less-loader": "^5.0.0",
    "lint-staged": "^9.2.1",
    "prettier": "^1.18.2",
    "react-app-rewired": "^2.1.3",
    "react-testing-library": "^8.0.1",
    "redux-devtools-extension": "^2.13.8",
    "redux-logger": "^3.0.6",
    "source-map-explorer": "^2.0.1"
  },
  "scripts": {
    "build-sw": "node ./src/sw-build.js",
    "clean-cra-sw": "rm -f build/precache-manifest.*.js && rm -f build/service-worker.js",
    "analyze": "source-map-explorer 'build/static/js/*.js'",
    "start": "PORT=3002 react-app-rewired start",
    "build": "react-app-rewired build && npm run build-sw && npm run clean-cra-sw",
    "test": "react-app-rewired test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

Can someone please tell me if I'm missing something here?

标签: reactjscreate-react-appservice-worker

解决方案


推荐阅读