最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Offline Pages with Service-Worker React - Stack Overflow

programmeradmin6浏览0评论

I am totally new to service-workers! I am having a react site running in the localhost and about to be deployed. I am following the code specified here.

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 default function register () {
  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)
    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 
      return
    }

    window.addEventListener('load', () => {
      const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`

      if (!isLocalhost) {
        // Is not local host. Just register service worker
        registerValidSW(swUrl)
      } else {
        // This is running on localhost. Lets check if a service worker still exists or not.
        checkValidServiceWorker(swUrl)
      }
    })
  }
}

function registerValidSW (swUrl) {
  navigator.serviceWorker
    .register(swUrl)
    .then(registration => {
      registration.onupdatefound = () => {
        const installingWorker = registration.installing
        installingWorker.onstatechange = () => {
          if (installingWorker.state === 'installed') {
            if (navigator.serviceWorker.controller) {
              // At this point, the old content will have been purged and
              // the fresh content will have been added to the cache.
              // It's the perfect time to display a "New content is
              // available; please refresh." message in your web app.
              console.log('New content is available; please refresh.')
            } 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.')
            }
          }
        }
      }
    })
    .catch(error => {
      console.error('Error during service worker registration:', error)
    })
}

function checkValidServiceWorker (swUrl) {
  // 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.
      if (
        response.status === 404 ||
        response.headers.get('content-type').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)
      }
    })
    .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()
    })
  }
}

This only works in Production but, I want the offline-site to run in the localhost and also cache only the base route ie /. I don't want to cache any other pages.
Any help is greatly appreciated !
Thanks!

I am totally new to service-workers! I am having a react site running in the localhost and about to be deployed. I am following the code specified here.

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 default function register () {
  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)
    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./facebookincubator/create-react-app/issues/2374
      return
    }

    window.addEventListener('load', () => {
      const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`

      if (!isLocalhost) {
        // Is not local host. Just register service worker
        registerValidSW(swUrl)
      } else {
        // This is running on localhost. Lets check if a service worker still exists or not.
        checkValidServiceWorker(swUrl)
      }
    })
  }
}

function registerValidSW (swUrl) {
  navigator.serviceWorker
    .register(swUrl)
    .then(registration => {
      registration.onupdatefound = () => {
        const installingWorker = registration.installing
        installingWorker.onstatechange = () => {
          if (installingWorker.state === 'installed') {
            if (navigator.serviceWorker.controller) {
              // At this point, the old content will have been purged and
              // the fresh content will have been added to the cache.
              // It's the perfect time to display a "New content is
              // available; please refresh." message in your web app.
              console.log('New content is available; please refresh.')
            } 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.')
            }
          }
        }
      }
    })
    .catch(error => {
      console.error('Error during service worker registration:', error)
    })
}

function checkValidServiceWorker (swUrl) {
  // 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.
      if (
        response.status === 404 ||
        response.headers.get('content-type').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)
      }
    })
    .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()
    })
  }
}

This only works in Production but, I want the offline-site to run in the localhost and also cache only the base route ie /. I don't want to cache any other pages.
Any help is greatly appreciated !
Thanks!

Share Improve this question edited Jul 18, 2021 at 13:30 Muhammad Bilal Bangash 1,2362 gold badges10 silver badges27 bronze badges asked Jul 16, 2021 at 11:56 Ajit KumarAjit Kumar 4171 gold badge14 silver badges39 bronze badges 11
  • The service worker provided by cra doesn't work in development mode, as specified in its docs. :) as caching stuffs in development would cause issues. You would want your changes to hot-reload during development. If you want to test caching, just build the app, don't deploy it and test it using a static server like 'serve'. :) – Nike Lepz Commented Jul 18, 2021 at 13:35
  • Thanks! How can I cache only the / (base) page? – Ajit Kumar Commented Jul 18, 2021 at 13:39
  • 1 I don't think that's possible. Workbox will cache all the js files, which will include every ponents. So everything get's cached. – Nike Lepz Commented Jul 18, 2021 at 13:44
  • 1 But why would you even need that? – Nike Lepz Commented Jul 18, 2021 at 13:45
  • The thing is that I have:- / /settings routes (pages) and more ing... I only want the / page to be offline. I don't want the other pages to be offline-working. If it is not an issue (not taking much memory or something) then it's okay ! – Ajit Kumar Commented Jul 19, 2021 at 3:24
 |  Show 6 more ments

1 Answer 1

Reset to default 7 +50

Here's what you could do.

//this is your settings ponent
export default function Settings(props) {
  const [offline, setOffline] = useState(false);
  useEffect(() => {
    if(!navigator.onLine) setOffline(true);
  }, []);
  return (
    <>
    {offline && (
       <div>You are offline please connect to the internet</div>
       <button onClick={() => window.location.reload(true)}>Refresh</button>
    )}
    {!offline && (
      <div>This is my actual settings page</div>
    )}
    </>
  );
}

There you go, you have conditional rendering of your settings, on the basis of the user being offline. Or you could also try alerting the user when the fetch fails(the user is offline). Like below:

export default function Settings(props) {
  const handleAPICall = () => {
    fetch(YOUR_API_ENDPOINT).then(res => res.json()).then(data => useData(data)).catch(err => {
      setOffline(true);
    });
  }
  return (
    <>
     {offline && (
       <Alert timeout={'3s'}>You are offline please connect to the internet</Alert>
     )}
     <div>
        <p>This is my actual settings page</p>
        <button onClick={handleAPICall}>Fetch data</button>
     </div>
    </>
  );
    </>
  );
}

I hope you find one of these approaches applicable to you. See, making only a part of your web app offline was never the right approach to your problem in the first place. Sometimes it gets hard to ask the right questions because you yourself don't know what you want. But we have to keep grinding.

发布评论

评论列表(0)

  1. 暂无评论