我有一个GatsbyJS网站,我正在将其部署到Netlify。每当我导航到网站时,服务人员都会为我提供该网站的过时版本,并在后台发送请求,以便下次获得较新的版本。 我认为访问我的网站的用户看到的版本可能已经过了几天是不可接受的。我希望服务人员在网络可用的情况下获取新版本,并提供陈旧的服务。版本仅在离线模式下。我找不到有关如何实现此目标的任何文档。
基于this GatsbyJS doc和this Workbox doc,我认为应该将策略从staleWhileRevalidate
更改为networkFirst
。他们没有在任何地方提供完整的示例,因此我不得不猜测语法,而且看来我的猜测是不正确的。 谁能提供一个完整的示例,说明如何配置gatsby-plugin-offline以实现合理的行为?
这是一个很长的答案,所以我将其分为三部分。
Gatsby的脱机插件在后台使用Google的工作箱来进行预缓存和运行时缓存。 runtime caching strategies提供了不同的workbox-strategies package。这些包括stale-while-revalidate,cache first (cache falling back to network)和network first (network falling back to cache)。
gatsby-plugin-offline设置适当的caching strategies for different URLs。例如:
CacheFirst
,因为它们具有哈希的唯一URL,可以从缓存中安全地对其进行服务。StaleWhileRevalidate
,因为它们没有哈希的URL。注意:Gatsby docs for gatsby-plugin-offline(截至2020年5月)表示page-data.json文件使用
NetworkFirst
,但使用they actually useStaleWhileRevalidate
。
没有在gatsby-plugin-offline中设置的特定处理程序的文件(例如,首次加载的HTML文件)StaleWhileRevalidate
。该插件在后台使用use a cache first policy。注意:我实际上在文档中找不到对此的引用,只是我链接到的注释,但是我进行了实验,并且HTML页面确实由服务工作者缓存,尽管不在generateSW from workbox-build列表中。
所以,我认为用户看到陈旧页面的问题是因为首次加载的HTML和page-data.json文件都是由服务工作者从缓存中提供的。
使用runtimeCaching
和NetworkFirst
(或CacheFirst
)之间需要权衡取舍。 StaleWhileRevalidate
已优化,可获取准确的数据并略微降低速度。 NetworkFirst
和CacheFirst
直接从缓存中获取,因此针对性能进行了优化,但要以拥有最新数据为代价。两者都具有在网络中断的情况下具有弹性的优势。
因此,它可能取决于每个用例,取决于网站的类型,内容和受众。例如:
StaleWhileRevalidate
甚至是StaleWhileRevalidate
。CacheFirst
很合适。NetworkFirst
。我认为有两种主要方法可以解决此问题:有可用更新时刷新页面,或将缓存策略更改为NetworkFirst
。
Gatsby为此提供了一个NetworkFirst
挂钩。因此,您可以按原样保留默认的缓存优先行为,但可以使用此钩子刷新页面。
最简单的方法是在Service Worker中有更新时重新加载页面:
onServiceWorkerUpdateReady
但是,这可能是侵入性和无提示的,因此不一定有很好的用户体验。替代方法是提示用户进行更新。这就是onServiceWorkerUpdateReady
:
// gatsby-browser.js
export const onServiceWorkerUpdateReady = () => window.location.reload(true);
如果您不喜欢本机浏览器提示(我不喜欢!),那么第三种选择是通过某些自定义UI提示用户。类似于:
Gatsby docs suggests
注意:如果您沿着这条路线并实现为对话框,请
// gatsby-browser.js export const onServiceWorkerUpdateReady = () => { const answer = window.confirm( `This application has been updated. ` + `Reload to display the latest version?` ) if (answer === true) { window.location.reload() } }
。
我认为这是您所追求的,因为它回答了“只要网络可用,我希望服务人员获取新版本”。
使用gatsby-plugin-offline覆盖// gatsby-browser.js
import React from "react";
import ReactDOM from "react-dom";
export const onServiceWorkerUpdateReady = () => {
const root = document.body.appendChild(document.createElement("div"));
ReactDOM.render(
<div>
<p>
Acme has been updated in the background.
<br />
Refresh to see the latest version.
</p>
<button onClick={() => window.location.reload(true)}>
Refresh
</button>
<button onClick={() => document.body.removeChild(root)}>
Close
</button>
</div>,
root
);
};
,以更改运行时缓存策略以适当地使用make sure it's accessible。使用the workbox config属性执行类似操作:
NetworkFirst
这是gatsby-plugin-offline使用的默认运行时缓存,并进行了两次关键更改:以下规则都使用runtimeCaching
:
// gatsby-config.js
module.exports = {
plugins: [
{
resolve: `gatsby-plugin-offline`,
options: {
workboxConfig: {
runtimeCaching: [
{
urlPattern: /(\.js$|\.css$|static\/)/,
handler: `CacheFirst`,
},
{
urlPattern: /^https?:.*\/page-data\/.*\/(page-data|app-data)\.json$/,
handler: `NetworkFirst`,
options: {
networkTimeoutSeconds: 1,
},
},
{
urlPattern: /^https?:.*\.(png|jpg|jpeg|webp|svg|gif|tiff|js|woff|woff2|json|css)$/,
handler: `StaleWhileRevalidate`,
},
{
urlPattern: /^https?:\/\/fonts\.googleapis\.com\/css/,
handler: `StaleWhileRevalidate`,
},
{
urlPattern: /\/$/,
handler: `NetworkFirst`,
options: {
networkTimeoutSeconds: 1,
},
},
],
},
},
},
]
};
之类的东西。