GatsbyJS Service Worker配置不遵守“ networkFirst”,而是继续提供过时的数据

时间:2019-02-16 15:28:57

标签: caching service-worker browser-cache gatsby workbox

我有一个要部署到Netlify的GatsbyJS网站。每当我导航到网站时,服务人员都会为我提供该网站的过时版本,并在后台发送请求,以便下次获得较新的版本。 我认为访问我的网站的用户看到的版本可能已经过了几天是不可接受的。我希望只要网络可用,服务工作者就可以获取新版本,并且仅在离线模式下提供过时的版本。我找不到有关如何实现此目标的任何文档。

基于this GatsbyJS docthis Workbox doc,我认为应该将策略从staleWhileRevalidate更改为networkFirst。他们没有在任何地方提供完整的示例,因此我不得不猜测语法,而且看来我的猜测是不正确的。 谁能提供一个完整的示例,说明如何配置gatsby-plugin-offline以实现合理的行为?

1 个答案:

答案 0 :(得分:5)

这是一个很长的答案,所以我将其分为三部分。

问题

Gatsby的脱机插件在后台使用Google的工作箱来进行预缓存和运行时缓存。 runtime caching strategies提供的工作箱具有不同的workbox-strategies package。其中包括stale-while-revalidatecache first (cache falling back to network)network first (network falling back to cache)

gatsby-plugin-offline设置适当的caching strategies for different URLs。例如:

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

注意:Gatsby docs for gatsby-plugin-offline(截至2020年5月)说 page-data.json 文件使用NetworkFirst,但使用they actually use StaleWhileRevalidate

在gatsby-plugin-offline中未设置特定处理程序的文件(例如,首次加载的HTML文件)use a cache first policy。该插件在幕后使用generateSW from workbox-build。注意:我实际上在文档中找不到对此的引用,只是我链接到的注释,但是我进行了实验,并且HTML页面确实由服务工作者缓存,尽管不在runtimeCaching列表中。 / p>

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

您应该修复它

在使用NetworkFirstCacheFirst(或StaleWhileRevalidate)之间需要权衡。 NetworkFirst经过优化,可获取准确的数据并略微降低速度。 CacheFirstStaleWhileRevalidate直接从缓存中服务,因此针对性能进行了优化,但要以拥有最新数据为代价。两者都具有在网络中断的情况下具有弹性的优势。

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

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

如何修复

我认为有两种主要的解决方法:有可用更新时刷新页面,或将缓存策略更改为NetworkFirst

刷新页面

Gatsby为此提供了一个onServiceWorkerUpdateReady挂钩。因此,您可以按原样保留默认的缓存优先行为,但可以使用此挂钩刷新页面。

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

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

但是,这可能是侵入性的且无提示的,因此不一定有很好的用户体验。替代方法是提示用户进行更新。这就是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()
  }
}

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

注意:如果您沿这条路线并实现为对话框,请make sure it's accessible

使用NetworkFirst

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

使用gatsby-plugin-offline覆盖the workbox config,以更改运行时缓存策略以适当地使用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使用的默认运行时缓存,并进行了2个主要更改:以下规则都使用NetworkFirst