【问题标题】:Create-react-app reload on service worker update服务工作者更新时的 Create-react-app 重新加载
【发布时间】:2019-03-19 16:09:46
【问题描述】:

我想修改create-react-app service worker file 并实现弹出消息,如果新的 service worker 准备好激活,它将要求用户更新应用程序。我几乎完成了解决方案,但有一个陷阱。我想在用户确认服务工作者更新弹出窗口时重新加载应用程序,所以我在register 函数的末尾添加了一些代码,见下文:

export default function register(config) {
  if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) {
    // The URL constructor is available in all browsers that support SW.
    const publicUrl = new URL(process.env.PUBLIC_URL, window.location)
    if (publicUrl.origin !== window.location.origin) {
      // Our service worker won't work if PUBLIC_URL is on a different origin
      // from what our page is served on. This might happen if a CDN is used to
      // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
      return
    }

    window.addEventListener("load", () => {
      const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`

      if (isLocalhost) {
        // This is running on localhost. Lets check if a service worker still exists or not.
        checkValidServiceWorker(swUrl, config)

        // Add some additional logging to localhost, pointing developers to the
        // service worker/PWA documentation.
        navigator.serviceWorker.ready.then(() => {
          console.log(
            "This web app is being served cache-first by a service " +
              "worker."
          )
        })
      } else {
        // Is not local host. Just register service worker
        registerValidSW(swUrl, config)
      }

      let preventDevToolsReloadLoop
      navigator.serviceWorker.addEventListener("controllerchange", function() {
        // ensure refresh is called only once
        if (preventDevToolsReloadLoop) {
          return
        }

        preventDevToolsReloadLoop = true
        console.log("reload")
        window.location.reload(true)
      })
    })
  }
}

但问题是它在第一次访问时也会重新加载应用程序,当时还没有任何服务工作者。我该如何解决?

【问题讨论】:

  • 如何在本地状态下将 preventDevToolsReloadLoop 设置为 true。并在export default function register(config) { 之后在顶部取消设置

标签: reactjs service-worker create-react-app


【解决方案1】:

更新到react-scripts ^3.2.0。验证您是否拥有新版本的 serviceWorker.ts 或 .js。旧的称为registerServiceWorker.ts,注册函数不接受配置对象。请注意,此解决方案仅在您不延迟加载时才有效。

然后在 index.tsx 中:

  serviceWorker.register({
    onUpdate: registration => {
      alert('New version available!  Ready to update?');
      if (registration && registration.waiting) {
        registration.waiting.postMessage({ type: 'SKIP_WAITING' });
      }
      window.location.reload();
    }
  });

ServiceWorker.ts register()function 的最新版本接受带有回调函数的配置对象,我们可以在其中处理升级。如果我们发布一条消息SKIP_WAITING,这会告诉服务工作者停止等待并在下一次刷新后继续加载新内容。在此示例中,我使用 javascript 警报来通知用户。请用自定义吐司替换它。

postMessage 函数起作用的原因是因为 CRA 在后台使用 workbox-webpack-plugin,其中包括一个 SKIP_WAITING 侦听器。

更多关于 Service Workers

好指南:https://redfin.engineering/how-to-fix-the-refresh-button-when-using-service-workers-a8e27af6df68

CRA 问题讨论 Service Worker 缓存:https://github.com/facebook/create-react-app/issues/5316

如果你不使用CRA,可以直接使用workbox:https://developers.google.com/web/tools/workbox

【讨论】:

  • 我已经解决了,但是忘记发布解决方案了,谢谢! :-)
  • 非常干净的解决方案!谢谢!
【解决方案2】:

完成@jfbloom22's answer:

您可能想在检测到比普通 alert 更复杂的更新后询问用户,您需要确保 registration 对象在 React 的组件树中可用并将其保存以供使用在用户接受更新后(例如,通过单击按钮)。

作为一个选项,在组件中,您可以在document 等全局对象上创建自定义事件侦听器,并在onUpdate 回调传递给serviceWorker.register() 时触发此事件,并将resgistration 对象传递给它额外的数据。

这正是我最近发布的Service Worker Updater 所做的(一些自我推销)。要使用它,您只需:

  • 将其添加到依赖项中:
yarn add @3m1/service-worker-updater
  • 在您的index.js 中使用它:
import { onServiceWorkerUpdate } from '@3m1/service-worker-updater';

// ...

// There are some naming changes in newer Create React App versions
serviceWorkerRegistration.register({
  onUpdate: onServiceWorkerUpdate
});
  • 在您的一些 React 组件中使用它:
import React from 'react';
import { withServiceWorkerUpdater } from '@3m1/service-worker-updater';

const Updater = (props) => {
  const {newServiceWorkerDetected, onLoadNewServiceWorkerAccept} = props;
  return newServiceWorkerDetected ? (
    <>
      New version detected.
      <button onClick={ onLoadNewServiceWorkerAccept }>Update!</button>
    </>
  ) : null; // If no update is available, render nothing
}

export default withServiceWorkerUpdater(Updater);

【讨论】:

  • 我真的要感谢这个伟大的模块,我实现了它并且它完美地工作!
  • 嗨@AlexandreDanault!不客气;)
  • 出色的作品。但它的一个问题是,如果您在更新之前刷新标签,您不会收到另一条通知。
【解决方案3】:

尝试在installingWorker.state === 'installed'

中添加重载功能
     if (installingWorker.state === 'installed') {
        if (navigator.serviceWorker.controller) {
          // do something ..
        }
      }

【讨论】:

    猜你喜欢
    • 2019-10-01
    • 2018-01-22
    • 2019-12-15
    • 1970-01-01
    • 1970-01-01
    • 2019-02-05
    • 1970-01-01
    • 1970-01-01
    • 2017-07-02
    相关资源
    最近更新 更多