Skip to content

Commit b32c724

Browse files
committed
implement cache on front end navigation feature
1 parent 4ae98a6 commit b32c724

File tree

6 files changed

+54
-14
lines changed

6 files changed

+54
-14
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# CHANGELOG
22

3+
## 5.2.1
4+
5+
### Major
6+
7+
1. Implement cache on front end navigation feature
8+
39
## 5.2.0
410

511
### Major

README.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ module.exports = withPWA({
257257
- example: `[/chunks\/images\/.*$/]` - Don't precache files under `.next/static/chunks/images` (Highly recommend this to work with `next-optimized-images` plugin)
258258
- doc: Array of (string, RegExp, or function()). One or more specifiers used to exclude assets from the precache manifest. This is interpreted following the same rules as Webpack's standard exclude option.
259259
- dynamicStartUrl - if your start url returns different HTML document under different state (such as logged in vs. not logged in), this should be set to true.
260-
- default: true
260+
- default: `true`
261261
- recommend: set to **false** if your start url always returns same HTML document, then start url will be precached, this will help to speed up first load.
262262
- fallbacks - config precached routes to fallback when both cache and network not available to serve resources.
263263
- **if you just need a offline fallback page, simply create a `/_offline` page such as `pages/_offline.js` and you are all set, no configuration necessary**
@@ -267,6 +267,9 @@ module.exports = withPWA({
267267
- `fallbacks.audio` - fallback route for audio, default to none
268268
- `fallbacks.video` - fallback route for video, default to none
269269
- `fallbacks.font` - fallback route for font, default to none
270+
- cacheOnFrontEndNav - enable additional route cache when navigate between pages with `next/link` on front end. Checkout this [example](https://github.com/shadowwalker/next-pwa/tree/master/examples/cache-on-front-end-nav) for some context about why this is implemented.
271+
- default: `false`
272+
- note: this improve user experience on special use cases but it also adds some overhead because additional network call, I suggest you consider this as a trade off.
270273
- ~~subdomainPrefix: string - url prefix to allow hosting static files on a subdomain~~
271274
- ~~default: `""` - i.e. default with no prefix~~
272275
- ~~example: `/subdomain` if the app is hosted on `example.com/subdomain`~~

build-fallback-worker.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ const getFallbackEnvs = ({fallbacks, basedir}) => {
4040

4141
const buildFallbackWorker = ({ id, fallbacks, basedir, destdir, success, minify }) => {
4242
const envs = getFallbackEnvs({fallbacks, basedir})
43-
if (!envs) return
43+
if (!envs) return false
4444

4545
const name = `fallback-${id}.js`
4646
const fallbackJs = path.join(__dirname, `fallback.js`)
@@ -103,6 +103,8 @@ const buildFallbackWorker = ({ id, fallbacks, basedir, destdir, success, minify
103103
success({name, precaches: Object.values(envs).filter(v => !!v)})
104104
}
105105
})
106+
107+
return fallbacks
106108
}
107109

108110
module.exports = buildFallbackWorker

examples/cache-on-front-end-nav/README.md

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
[TOC]
66

7+
> **Since `[email protected]`, you can set `cacheOnFrontEndNav: true` in your `pwa` config to achieve the same result in this example, no other code needed.**
8+
79
This example demonstrates how to use `next-pwa` plugin to solve the issue when users refresh on a front end navigated route while offline and saw browser's connection lost page. This is an edge case which should not happen very often in normal network connectivity areas, however, this example should help you improve the users experience.
810

911
For context, `next.js` embraces both SSR and front end routing (typical SPA) to deliver smooth users experience. However, when a user navigate on the web app through `next/router` or `next/link` (Link component), the navigation is made through front end routing. Which means there is no HTTP GET request made to the server for that route, it only swap the react component to the new page and change the url showed on the url bar. This "fake" navigation is usually desired because it means users do not have to wait for network delay.

index.js

+5-9
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ module.exports = (nextConfig = {}) => ({
4040
buildExcludes = [],
4141
manifestTransforms = [],
4242
modifyURLPrefix = {},
43-
fallbacks = {},
43+
cacheOnFrontEndNav = false,
4444
subdomainPrefix, // deprecated, use basePath in next.config.js instead
4545
...workbox
4646
} = pwa
@@ -63,7 +63,7 @@ module.exports = (nextConfig = {}) => ({
6363
let basePath = options.config.basePath
6464
if (!basePath) basePath = '/'
6565

66-
let { runtimeCaching = defaultCache, scope = basePath } = pwa
66+
let { runtimeCaching = defaultCache, scope = basePath, fallbacks = {} } = pwa
6767
scope = path.posix.join(scope, '/')
6868

6969
// inject register script to main.js
@@ -73,7 +73,8 @@ module.exports = (nextConfig = {}) => ({
7373
__PWA_SW__: `'${_sw}'`,
7474
__PWA_SCOPE__: `'${scope}'`,
7575
__PWA_ENABLE_REGISTER__: `${Boolean(register)}`,
76-
__PWA_START_URL__: dynamicStartUrl ? `'${basePath}'` : undefined
76+
__PWA_START_URL__: dynamicStartUrl ? `'${basePath}'` : undefined,
77+
__PWA_CACHE_ON_FRONT_END_NAV__: `${Boolean(cacheOnFrontEndNav)}`
7778
})
7879
)
7980

@@ -155,7 +156,7 @@ module.exports = (nextConfig = {}) => ({
155156
}
156157

157158
if (fallbacks) {
158-
buildFallbackWorker({
159+
fallbacks = buildFallbackWorker({
159160
id: buildId,
160161
fallbacks,
161162
basedir: options.dir,
@@ -250,11 +251,6 @@ module.exports = (nextConfig = {}) => ({
250251
handler: 'NetworkFirst',
251252
options: {
252253
cacheName: 'start-url',
253-
expiration: {
254-
maxEntries: 1,
255-
maxAgeSeconds: 24 * 60 * 60 // 24 hours
256-
},
257-
networkTimeoutSeconds: 10,
258254
plugins: [{
259255
// mitigate Chrome 89 auto offline check issue
260256
// blog: https://developer.chrome.com/blog/improved-pwa-offline-detection/

register.js

+34-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Workbox } from 'workbox-window'
22

33
if (typeof window !== 'undefined' && 'serviceWorker' in navigator) {
4-
const initWorkbox = function(e) {
4+
const initWorkbox = function() {
55
window.workbox = new Workbox(__PWA_SW__, { scope: __PWA_SCOPE__ })
66

77
if(__PWA_ENABLE_REGISTER__) {
@@ -10,10 +10,41 @@ if (typeof window !== 'undefined' && 'serviceWorker' in navigator) {
1010
}
1111

1212
if(__PWA_START_URL__) {
13-
caches.open('start-url').then(function(cache) {
14-
cache.add(__PWA_START_URL__).then(initWorkbox)
13+
fetch(__PWA_START_URL__).then(function(response) {
14+
if (!response.ok && !response.redirected) return
15+
return caches.open('start-url').then(function(cache) {
16+
return cache.put(__PWA_START_URL__, response).then(initWorkbox)
17+
})
1518
})
1619
} else {
1720
initWorkbox()
1821
}
22+
23+
if(__PWA_CACHE_ON_FRONT_END_NAV__) {
24+
const cacheOnFrontEndNav = function(url) {
25+
if (!window.navigator.onLine) return
26+
if (__PWA_START_URL__ && url === __PWA_START_URL__) return
27+
caches.open('others').then(cache =>
28+
cache.match(url, {ignoreSearch: true}).then(res => {
29+
if (!res) return cache.add(url)
30+
})
31+
)
32+
}
33+
34+
const pushState = history.pushState
35+
history.pushState = function () {
36+
pushState.apply(history, arguments)
37+
cacheOnFrontEndNav(arguments[0].url)
38+
}
39+
40+
const replaceState = history.replaceState
41+
history.replaceState = function () {
42+
replaceState.apply(history, arguments)
43+
cacheOnFrontEndNav(arguments[0].url)
44+
}
45+
46+
window.addEventListener('online', () => {
47+
cacheOnFrontEndNav(window.location.pathname)
48+
})
49+
}
1950
}

0 commit comments

Comments
 (0)