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

Add simplified service worker invalidation #2551

Merged
merged 2 commits into from
Jun 27, 2017
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Add service worker invalidation
  • Loading branch information
ro-savage committed Jun 27, 2017
commit aea0feed18f9b05332a78943085a0ddb753a419c
73 changes: 49 additions & 24 deletions packages/react-scripts/template/src/registerServiceWorker.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,36 +21,61 @@ export default function register() {

window.addEventListener('load', () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
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.');
}
}
};
};

fetch(swUrl)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Throwing this out there: the overhead of this extra request would be reduced if there were an additional check that only caused it to execute when you're on localhost or the equivalent. This PR would then solve the "zombie" SW on localhost problem immediately, but would not attempt to unregister a service worker on a production server.

// Inside the window.onload handler:

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}$/
  )
);

if (isLocalhost) {
  fetch(swUrl) // ...the rest of your logic here...
} else {
  registerValidSW(swUrl); // Avoid the extra fetch() outside of localhost.
}

It's unclear how w3c/ServiceWorker#204 will get resolved, but there has been some opposition to automatically unregistering production servers on an HTTP 404 response. My hope is that the code that comes out of this PR will evolve (post-merge) to match whatever's decided upon in that issue, providing equivalent behavior prior to the actual native browser implementation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Jeff!

.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
) {
navigator.serviceWorker.ready.then(registration => {
registration.unregister().then(() => {
window.location.reload();
});
});
} else {
registerValidSW(swUrl);
}
})
.catch(error => {
console.error('Error during service worker registration:', error);
.catch(() => {
console.log(
'No internet connection found. App is running in offline mode.'
);
});
});
}
}

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);
});
}

export function unregister() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then(registration => {
Expand Down