首页 > 解决方案 > GatsbyJS Service Worker 配置不尊重“networkFirst”,继续提供陈旧数据

问题描述

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

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

标签: cachingservice-workerbrowser-cachegatsbyworkbox

解决方案


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

问题

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

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

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

注意:gatsby-plugin-offline 的 Gatsby 文档(截至 2020 年 5 月)说page-data.json文件使用NetworkFirst它们实际上使用StaleWhileRevalidate.

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

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

你应该修理它吗

NetworkFirst使用and CacheFirst(or StaleWhileRevalidate)之间有一个权衡。NetworkFirst针对准确的数据和速度的轻微损失进行了优化。鉴于直接从缓存CacheFirstStaleWhileRevalidate提供服务,因此以牺牲最新数据为代价对性能进行了优化。在网络中断的情况下,两者都具有弹性优势。

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

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

如何修复它

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

刷新页面

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

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

// gatsby-browser.js
export const onServiceWorkerUpdateReady = () => window.location.reload(true);

但是,这可能是侵入性的且不经提示的,因此不一定是出色的用户体验。另一种方法是提示用户更新。这就是盖茨比文档的建议

// 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()
  }
}

如果您不是本机浏览器提示的粉丝(我不是!),那么第三种选择是通过一些自定义 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
    );
};

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

使用网络优先

我认为这就是您所追求的,因为它回答了您的“只要网络可用,我希望 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,
                            },
                        },
                    ],
                },
            },
        },
    ]
};

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


推荐阅读