GatsbyJS Service Worker 配置不尊重“networkFirst”,继续提供陈旧数据

Att*_*nen 2 caching browser-cache service-worker gatsby workbox

我有一个 GatsbyJS 网站,我要部署到 Netlify。每当我导航到我的网站时,Service Worker 都会为我提供该网站的旧版本并在后台发送请求,以便我下次获得更新的版本。我认为访问我网站的用户看到的版本可能是几天前的版本是不可接受的。只要网络可用,我希望 Service Worker 获取新版本,并且仅在离线模式下提供旧版本。我找不到有关如何实现这一点的任何文档。

基于此GatsbyJS文档这个针线文档我想它应该努力从改变策略staleWhileRevalidatenetworkFirst。他们没有在任何地方提供完整的例子,所以我不得不猜测语法,看起来我的猜测不正确。谁能提供一个完整的示例,说明如何配置 gatsby-plugin-offline 以实现合理的行为?

Ian*_*dge 10

这是一个很长的答案,所以我把它分成了 3 部分。

问题

Gatsby 的离线插件在后台使用 Google 的工作箱进行预缓存和运行时缓存。Workbox 具有由workbox-strategies 包提供的不同运行时缓存策略。这些包括stale-while-revalidate缓存优先(缓存回退到网络)网络优先(网络回退到缓存)

gatsby-plugin-offline为不同的 URL设置适当的缓存策略。例如:

  • 静态目录下的CSS、JS和文件使用 CacheFirst它们,因为它们具有散列的唯一 URL,并且可以安全地从缓存中提供。
  • 使用 page-data.json文件StaleWhileRevalidate是因为它们没有散列的 URL。

注:盖茨比为文档盖茨比-插件脱机(截至5月到2020年)说,页面data.json文件使用NetworkFirst,但他们实际使用StaleWhileRevalidate

在 gatsby-plugin-offline 中没有设置特定处理程序的文件(例如首次加载的 HTML 文件)使用缓存优先策略。该插件使用来自 workbox-build 的 generateSW。注意:我实际上无法在文档中找到对此的引用,只是我链接到的评论,但我做了实验,并且 HTML 页面肯定由服务工作者缓存,尽管不在runtimeCaching列表中。

所以我认为您的用户看到陈旧页面的问题是因为首次加载时的 HTML 和page-data.json文件都是由服务工作者从缓存中提供的。

你应该修理它吗

使用NetworkFirstCacheFirst(或StaleWhileRevalidate)之间存在折衷。NetworkFirst针对准确的数据和轻微的速度代价进行了优化。鉴于CacheFirstStaleWhileRevalidate直接从缓存,以便在具有最先进的最新数据为代价的性能优化服务。在网络中断的情况下,两者都具有弹性的好处。

因此,它可能取决于每个用例,具体取决于网站类型、内容和受众。例如:

  • 内容不经常更新的博客可能可以安全地使用StaleWhileRevalidate,甚至CacheFirst用于个人帖子。
  • 一个网站,你知道你的大部分用途是通过快速、有线、可靠的互联网连接使用台式电脑,这意味着使用 NetworkFirst可能是合适的。
  • 一个显示时间敏感内容的网站,您希望在其中查看最新内容,使用NetworkFirst.

如何修复

我认为有两种主要方法可以解决这个问题:更新可用时刷新页面,或者将缓存策略更改为NetworkFirst.

刷新页面

盖茨比onServiceWorkerUpdateReady为此提供了一个钩子。所以你可以保持默认的缓存优先行为,但使用这个钩子来刷新页面。

最简单的方法是在 service worker 有更新时重新加载页面:

// gatsby-browser.js
export const onServiceWorkerUpdateReady = () => window.location.reload(true);
Run Code Online (Sandbox Code Playgroud)

但是,这可能是侵入性的并且没有提示,因此不一定是很好的用户体验。另一种方法是提示用户更新。这是Gatsby 文档的建议

// 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()
  }
}
Run Code Online (Sandbox Code Playgroud)

如果您不喜欢本机浏览器提示(我不喜欢!),那么第三个选项是通过一些自定义 UI 提示用户。就像是:

// 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&nbsp;background.
                <br />
                Refresh to see the latest&nbsp;version.
            </p>
            <button onClick={() => window.location.reload(true)}>
                Refresh
            </button>
            <button onClick={() => document.body.removeChild(root)}>
                Close
            </button>
        </div>,
        root
    );
};
Run Code Online (Sandbox Code Playgroud)

注意:如果您要沿着这条路线走并作为对话框实现,那么请确保它是可访问的

使用网络优先

我认为这就是您所追求的,因为它回答了您的“只要网络可用,我希望 Service Worker 获取新版本”。

使用 gatsby-plugin-offline覆盖工作箱配置以更改运行时缓存策略以NetworkFirst适当使用。使用该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,
                            },
                        },
                    ],
                },
            },
        },
    ]
};
Run Code Online (Sandbox Code Playgroud)

这是 gatsby-plugin-offline 使用的默认 runtimeCaching 有 2 个关键更改:以下规则都使用NetworkFirst