IT虾米网

ios之ReactJs PWA 未在 iOS 上更新

Free-Thinker 2026年09月08日 程序员 57 0

我正在构建 ReactJs PWA,但在检测 iOS 上的更新时遇到问题。

在 Android 上,一切都运行良好,所以我想知道这一切是否与 iOS 对 PWA 的支持有关,或者是否我对 Service Worker 的实现不好。

这是我到目前为止所做的事情:

构建流程和托管

我的应用程序是使用 webpack 构建并托管在 AWS 上。大多数文件(js/css)都是在其名称中使用一些哈希值构建的,这些哈希值是根据其内容生成的。对于那些不是的(应用程序 list 、index.html、sw.js),我确保AWS为它们提供一些缓存控制 header 以防止任何缓存。一切都通过 https 提供。

服务人员

我使这个尽可能简单:除了为我的应用程序外壳预缓存之外,我没有添加任何缓存规则:

workbox.precaching.precacheAndRoute(self.__precacheManifest || []); 

服务人员注册

Service Worker 的注册发生在主 ReactJs App 组件中的 componentDidMount() 生命周期 Hook 中:

componentDidMount() { 
  if ('serviceWorker' in navigator) { 
    navigator.serviceWorker.register('/sw.js') 
      .then((reg) => { 
        reg.onupdatefound = () => { 
          this.newWorker = reg.installing; 
          this.newWorker.onstatechange = () => { 
            if (this.newWorker.state === 'installed') { 
              if (reg.active) { 
                // a version of the SW is already up and running 
 
                /* 
                  code omitted: displays a snackbar to the user to manually trigger 
                  activation of the new SW. This will be done by calling skipWaiting() 
                  then reloading the page 
                */ 
              } else { 
                // first service worker registration, do nothing 
              } 
            } 
          }; 
        }; 
      }); 
  } 
} 

Service Worker 生命周期管理

根据Google documentation about service workers ,当导航到范围内的页面时,应该检测到新版本的 Service Worker。但作为单页应用程序,一旦加载应用程序,就不会发生硬导航。

我找到的解决方法是 Hook 到react-router并监听路由更改,然后手动要求注册的服务工作人员更新自身:

const history = createBrowserHistory(); // from 'history' node package 
history.listen(() => { 
  if ('serviceWorker' in navigator) { 
    navigator.serviceWorker 
      .getRegistration() 
      .then((reg) => { 
 
        if (!reg) { 
          return null; 
        } 
 
        reg.update(); 
      }); 
  } 
}); 
 

实际行为

在上面显示的代码中到处扔一堆alert(),这就是我观察到的:

  1. 将 pwa 添加到主屏幕后首次打开时,Service Worker 在 Android 和 iOS 上按预期注册
  2. 在保持应用程序打开的同时,我在 AWS 上部署了新版本。感谢我的历史监听器,在应用程序中导航会触发手动更新。找到新版本,并在后台安装。然后我的 snackbar 就会显示出来,我可以触发切换到新的软件。
  3. 现在我关闭应用程序并在 AWS 上部署新版本。再次打开应用程序时:
    • 在 Android 上,当 Android 重新加载页面时,会立即发现更新
    • iOS 没有,因此我需要在应用程序中导航以查找历史监听器以触发更新搜索。这样做时,会发现更新
    • 此后,对于两个操作系统,我的 snackbar 都会显示,我可以触发切换到新的软件
  4. 现在我关闭应用程序并关闭手机。部署新版本后,我再次启动它们并打开应用程序:
    • 在 Android 上,就像以前一样,页面会重新加载以检测更新,然后显示 snackbar 等。
    • 在 iOS 上,我在应用内导航,我的监听器会触发更新搜索。 但这一次,新版本从未找到,我的 onupdatefound 事件处理程序也从未触发
<小时 />

阅读this post on Medium from Maximiliano Firtman看来,iOS 12.2 为 PWA 带来了新的生命周期。据他介绍,当应用程序长时间处于空闲状态或在设备重新启动期间,应用程序状态以及页面都会被终止。

我想知道这是否是我的问题的根本原因,但到目前为止我还没有找到任何人遇到同样的问题。

请您参考如下方法:

经过大量的挖掘和调查,我终于找到了我的问题所在。

根据我的观察,我认为 Android 和 iOS 处理 PWA 生命周期以及 Service Worker 的方式略有不同。

在 Android 上,重新启动后启动应用程序时,启动应用程序和搜索 Service Worker 的更新(由于重新加载页面时发生的硬导航)看起来像是 2 个并行完成的任务。通过这样做,应用程序有足够的时间订阅现有的 Service Worker,并在找到新版本的 Service Worker 之前定义一个 onupdatefound() 处理程序。

另一方面,对于 iOS,似乎当您在设备重新启动后启动应用程序时(或长时间未使用它后,请参阅主要主题中链接的 Medium 文章),iOS 会触发搜索在启动您的应用程序之前进行更新。如果发现更新,则会安装该更新,并在应用实际启动之前进入“等待”状态。这可能是显示闪屏时发生的情况...... 因此,最后,当您的应用程序最终启动并且您订阅现有的 Service Worker 来定义您的 onupdatefound() 处理程序时,更新已经安装并正在等待控制客户端。

这是我注册服务 worker 的最终代码:

componentDidMount() { 
  if ('serviceWorker' in navigator) { 
    navigator.serviceWorker.register('/sw.js') 
      .then((reg) => { 
 
        if (reg.waiting) { 
          // a new version is already waiting to take control 
          this.newWorker = reg.waiting; 
 
          /* 
            code omitted: displays a snackbar to the user to manually trigger 
            activation of the new SW. This will be done by calling skipWaiting() 
            then reloading the page 
          */ 
        } 
 
        // handler for updates occuring while the app is running, either actively or in the background 
        reg.onupdatefound = () => { 
          this.newWorker = reg.installing; 
 
          this.newWorker.onstatechange = () => { 
            if (this.newWorker.state === 'installed') { 
              if (reg.active) { 
                // a version of the SW already has control over the app 
 
                /* 
                  same code omitted 
                */ 
              } else { 
                // very first service worker registration, do nothing 
              } 
            } 
          }; 
        }; 
      }); 
  } 
} 

注意:

我还摆脱了我用来触发搜索每次路线更改更新的历史记录的监听器,因为这似乎有点矫枉过正。 现在,我依靠页面可见性 API 在每次应用获得焦点时触发此搜索:

// this function is called in the service worker registration promise, providing the ServiceWorkerRegistration instance 
const registerPwaOpeningHandler = (reg) => { 
    let hidden; 
    let visibilityChange; 
    if (typeof document.hidden !== 'undefined') { // Opera 12.10 and Firefox 18 and later support 
        hidden = 'hidden'; 
        visibilityChange = 'visibilitychange'; 
    } else if (typeof document.msHidden !== 'undefined') { 
        hidden = 'msHidden'; 
        visibilityChange = 'msvisibilitychange'; 
    } else if (typeof document.webkitHidden !== 'undefined') { 
        hidden = 'webkitHidden'; 
        visibilityChange = 'webkitvisibilitychange'; 
    } 
 
    window.document.addEventListener(visibilityChange, () => { 
        if (!document[hidden]) { 
            // manually force detection of a potential update when the pwa is opened 
            reg.update(); 
        } 
    }); 
 
    return reg; 
}; 


评论关闭
IT虾米网

微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!