返回

Chrome PWA 跳转难题:两种高效解决方案

Android

强制从 Chrome 打开 PWA 的问题及解决方法

PWA(渐进式 Web 应用)提供接近原生应用的用户体验。不过,在某些情况下,从 Chrome 浏览器中点击链接后,PWA 的导航行为可能会遇到问题。举个例子,一个应用先加载初始页面,并通过 API 请求获得新 URL,然后尝试在此 PWA 内跳转至该新 URL,此时可能无法正常工作,页面会停留在加载状态,并最终失败。

问题分析

当用户通过 Chrome 浏览器点击 PWA 链接时,浏览器会尝试在已安装的 PWA 环境中打开页面。 如果PWA本身加载时处于"loading" 页面,又因为随后的API响应更改 PWA 的 URL, 导致初始加载和重定向之间的潜在冲突。 特别是像上面的场景:利用 window.open打开一个loading页面,然后在login api中对 window的location.href赋值。因为在 PWA 中,window.open 和后续的 location.href 变化可能需要正确地协调处理,否则可能导致页面无法正常导航。 这个问题的本质是:初始的window.open 调用和其后基于异步操作 loginApi结果修改 location.href的操作, 无法顺利地在 PWA 的环境中传递下去。 这不是浏览器的故障,而是PWA环境中需要进行特定适配。

解决方案一:使用 Service Worker 进行拦截

Service Worker 是一种在后台运行的脚本,可以拦截网络请求和管理缓存。 我们可以利用 Service Worker 拦截初始的导航请求,并根据 target_url进行重定向。这样就能在 PWA 中安全可靠地处理 URL 变化。

实现步骤:

  1. 注册 Service Worker: 如果你的项目未使用Service Worker, 你需要在PWA 入口进行 Service Worker注册。 大部分 PWA 库已经集成该操作, 你需要做的就是引入Service Worker脚本。 例如:next-pwa库提供了这个特性, 你需要正确配置next.config.js文件。
  2. 监听 Service Worker 中 fetch 事件: 拦截发送给 PWA 的初始请求。
  3. 判断是否需要重定向: 检查初始 URL 中是否存在指示需跳转的参数,例如 accessToken
  4. 处理跳转: 当 API 调用成功后,将 URL 保存到客户端存储(例如 localStorage),同时告诉 Service Worker 在处理 PWA 页面初始化请求的时候直接使用这个跳转链接,而不是 PWA 加载初始地址,进行重定向,完成 URL 跳转。

代码示例(service-worker.js):

// 这部分内容通常由 `next-pwa` 生成, 请参考你使用的PWA库的文档
// const CACHE_NAME = "app-cache-v1";

// self.addEventListener("install", event => {
//   console.log('Service Worker Installing')
// })

self.addEventListener('fetch', (event) => {
    event.respondWith(
      caches.match(event.request).then((cachedResponse) => {
          if (cachedResponse) {
            return cachedResponse;
          }

        // 如果请求的是页面跳转, 则从 localStorage 获取上一步的 url 进行重定向
          if(event.request.mode === 'navigate') {
            const storedUrl = localStorage.getItem('redirectUrl');
            if(storedUrl) {
              localStorage.removeItem('redirectUrl'); // 处理完毕后移除 localStorage 中保存的 url,避免下次跳转错误。
                return  Response.redirect(storedUrl, 302)
              }
          }
            return fetch(event.request);

        })
    )
  }
)

前端代码修改(修改部分逻辑):

   const openWindow = window.open(
      `/loading?accessToken=${access_token}`,
      'launch',
      'width=1025px,height=600px'
    );
    addWindow(openWindow);
    loginApi(openWindow, payload)

if (statusTranspointApi.status === 200) {
   localStorage.setItem("redirectUrl", data.target_url);
    openWindow.location.href = '/';  // 设置一个过渡 URL,实际上我们不期望从这里跳转到其他链接
}

操作步骤:

  1. 修改 service-worker.js 代码,或者你当前项目使用的service worker 文件,添加上述fetch事件的监听处理逻辑。
  2. 前端页面将 data.target_url 保存到 localStorage 中。然后修改 openWindow.location.href 值。
  3. 用户点击 Chrome 中的链接,PWA 启动后, Service Worker 监听到页面的初始化跳转事件,就会执行 localStorage 中读取的URL进行跳转。
  4. 完成跳转。

解决方案二:使用 PWA API 的 navigation 事件

部分浏览器为 PWA 提供了专门的导航 API。可以通过 navigation 事件监听页面的加载和导航过程,从而进行精确的控制。该方案针对的场景是,PWA已经启动,且在进行 PWA 的导航跳转时候,需要被PWA本身接管的场景。由于目标url 来自 api 返回,这更像在pwa中操作内部导航。

实现步骤:

  1. 监听 navigator.serviceWorker.ready 事件: 确保 Service Worker 已激活。
  2. 监听 navigation 事件: 使用navigator.serviceWorker.controller 监听 PWA 内的导航事件。
  3. 处理导航事件: 拦截特定状态(例如收到 API 响应后),使用event.navigate()方法修改URL。

代码示例(前端逻辑):


navigator.serviceWorker.ready.then(registration => {
  registration.navigationPreload?.enable()  // 若浏览器支持 navigationPreload API则使用它
    // 利用service worker 获取导航
  registration.addEventListener('message', event => {
      if (event.data && event.data.type === 'navigateToUrl') {
        if(navigator.navigation && typeof navigator.navigation.navigate === 'function'){ // 确认 browser API 支持。
          navigator.navigation.navigate(event.data.url)
         } else{
         window.location.href = event.data.url; // 老旧的浏览器使用传统的方式进行跳转。
         }
       }
    });

   })


    const openWindow = window.open(
              `/loading?accessToken=${access_token}`,
              'launch',
              'width=1025px,height=600px'
            );
            addWindow(openWindow);
            loginApi(openWindow, payload);


if (statusTranspointApi.status === 200) {
     navigator.serviceWorker.controller.postMessage({
            type: 'navigateToUrl',
            url:  data.target_url
           }); // 通知 service worker 执行 url跳转操作
   }


// service worker (与 方案1 中示例略有不同)
self.addEventListener("message", event => {
    if(event.data && event.data.type ==="navigateToUrl") {
       self.clients.matchAll().then(clients => { // 使用 all 避免不兼容。
              clients.forEach(client =>{
               client.postMessage({type:"navigateToUrl", url:event.data.url})
            })
       })
     }

})

操作步骤:

  1. 在前端代码中添加 Service Worker 的监听。
  2. 在 api 请求成功后,通过 postMessage 和 Service Worker 通信传递url跳转信息,最终实现URL更新。
  3. 在 Service Worker 中添加接受到页面消息后, 遍历所有可用 client 并 发送 message , 进行 PWA URL 更新跳转。

安全建议:

  • 使用HTTPS以确保 Service Worker 的安全性。
  • 谨慎处理来自 localStorage 或其他本地存储的数据,避免 XSS 攻击。

总的来说,通过合理运用 Service Worker 或者 导航 API,可以在 PWA 中更好地管理 URL 重定向。 选择哪一种方案,主要取决于项目需求、浏览器兼容性以及个人偏好。希望以上信息对你有所帮助。