Skip to content

Commit

Permalink
#276. Add Service Worker in prod
Browse files Browse the repository at this point in the history
  • Loading branch information
jaredpalmer committed May 22, 2017
1 parent d80fb21 commit e5a0248
Show file tree
Hide file tree
Showing 12 changed files with 625 additions and 39 deletions.
15 changes: 15 additions & 0 deletions examples/basic/public/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"short_name": "Razzle App",
"name": "Razzle Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "192x192",
"type": "image/png"
}
],
"start_url": "./index.html",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
4 changes: 3 additions & 1 deletion examples/basic/src/client.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import App from './App';
import React from 'react';
import registerServiceWorker from './registerServiceWorker';
import { render } from 'react-dom';
import App from './App';

render(<App />, document.getElementById('root'));
registerServiceWorker();

if (module.hot) {
module.hot.accept();
Expand Down
51 changes: 51 additions & 0 deletions examples/basic/src/registerServiceWorker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// In production, we register a service worker to serve assets from local cache.

// 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 the "N+1" visit to a page, since previously
// cached resources are updated in the background.

// To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
// This link also includes instructions on opting out of this behavior.

export default function register() {
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
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.');
}
}
};
};
})
.catch(error => {
console.error('Error during service worker registration:', error);
});
});
}
}

export function unregister() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then(registration => {
registration.unregister();
});
}
}
18 changes: 10 additions & 8 deletions examples/basic/src/server.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import App from './App';
import React from 'react';
import express from 'express';
import path from 'path';
import React from 'react';
import { renderToString } from 'react-dom/server';
import App from './App';

const assets = require(process.env.RAZZLE_ASSETS_MANIFEST);

Expand All @@ -15,12 +15,14 @@ server
const markup = renderToString(<App />);
res.send(
`<!doctype html>
<html lang="">
<head>
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
<meta charSet='utf-8' />
<title>Welcome to Razzle</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<link rel="manifest" href="${process.env.PUBLIC_URL}/manifest.json">
<link rel="shortcut icon" href="${process.env.PUBLIC_URL}/favicon.ico">
<title>Razzle App</title>
${assets.client.css ? `<link rel="stylesheet" href="${assets.client.css}">` : ''}
<script src="${assets.client.js}" defer></script>
</head>
Expand Down
15 changes: 15 additions & 0 deletions packages/create-razzle-app/templates/default/public/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"short_name": "Razzle App",
"name": "Razzle Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "192x192",
"type": "image/png"
}
],
"start_url": "./index.html",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// In production, we register a service worker to serve assets from local cache.

// 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 the "N+1" visit to a page, since previously
// cached resources are updated in the background.

// To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
// This link also includes instructions on opting out of this behavior.

export default function register() {
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
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.');
}
}
};
};
})
.catch(error => {
console.error('Error during service worker registration:', error);
});
});
}
}

export function unregister() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then(registration => {
registration.unregister();
});
}
}
14 changes: 8 additions & 6 deletions packages/create-razzle-app/templates/default/src/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ server
} else {
res.status(200).send(
`<!doctype html>
<html lang="">
<head>
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
<meta charSet='utf-8' />
<title>Welcome to Razzle</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<link rel="manifest" href="${process.env.PUBLIC_URL}/manifest.json">
<link rel="shortcut icon" href="${process.env.PUBLIC_URL}/favicon.ico">
<title>Razzle App</title>
${assets.client.css ? `<link rel="stylesheet" href="${assets.client.css}">` : ''}
<script src="${assets.client.js}" defer></script>
</head>
Expand Down
32 changes: 32 additions & 0 deletions packages/razzle/config/create-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const webpack = require('webpack');
const nodeExternals = require('webpack-node-externals');
const AssetsPlugin = require('assets-webpack-plugin');
const StartServerPlugin = require('start-server-webpack-plugin');
const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin');
const FriendlyErrorsPlugin = require('./FriendlyErrorsPlugin');
const autoprefixer = require('autoprefixer');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
Expand Down Expand Up @@ -319,6 +320,37 @@ module.exports = (
new ExtractTextPlugin({
filename: 'static/css/[name].[contenthash:8].css',
}),
new SWPrecacheWebpackPlugin({
// By default, a cache-busting query parameter is appended to requests
// used to populate the caches, to ensure the responses are fresh.
// If a URL is already hashed by Webpack, then there is no concern
// about it being stale, and the cache-busting can be skipped.
dontCacheBustUrlsMatching: /\.\w{8}\./,
filename: 'service-worker.js',
logger(message) {
if (message.indexOf('Total precache size is') === 0) {
// This message occurs for every build and is a bit too noisy.
return;
}
console.log(message);
},
minify: true,
navigateFallback: dotenv.raw.PUBLIC_URL + '/index.html',
staticFileGlobsIgnorePatterns: [
/\.map$/,
/asset-manifest\.json$/,
/assets\.json$/,
],
// Work around Windows path issue in SWPrecacheWebpackPlugin:
// https://github.com/facebookincubator/create-react-app/issues/2235
stripPrefix: paths.appBuild.replace(/\\/g, '/') + '/',
}),
// Moment.js is an extremely popular library that bundles large locale files
// by default due to how Webpack interprets its code. This is a practical
// solution that requires the user to opt into importing specific locales.
// https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
// You can remove this if you don't use Moment.js:
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
];
}
}
Expand Down
1 change: 1 addition & 0 deletions packages/razzle/config/env.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ function getClientEnvironment(target, options) {
HOST: process.env.HOST || options.host || 'localhost',
RAZZLE_ASSETS_MANIFEST: paths.appManifest,
BUILD_TARGET: target === 'web' ? 'client' : 'server',
PUBLIC_URL: process.env.PUBLIC_URL || '',
// The public dir changes between dev and prod, so we use an environment
// variable available to users.
RAZZLE_PUBLIC_DIR: process.env.NODE_ENV === 'production'
Expand Down
1 change: 1 addition & 0 deletions packages/razzle/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"react-dev-utils": "^1.0.2",
"start-server-webpack-plugin": "^2.2.0",
"style-loader": "^0.17.0",
"sw-precache-webpack-plugin": "^0.9.2",
"url-loader": "^0.5.8",
"webpack": "^2.5.1",
"webpack-dev-server": "^2.4.5",
Expand Down
Loading

0 comments on commit e5a0248

Please sign in to comment.