浏览器ServiceWorker特性带来的巨坑:200 OK (from service worker)

今日日常编码中遇到一个反复折腾的问题:

站点A:zeeklog.com/

站点B:zeeklog.com/basic

问题表现:

当访问站点B时, 正常加载站点B资源和打开站点B页面, 奇怪的事情来了, 当页面刷新后,页面自动跑到了站点A。

怀疑问题一:一开始以为是nginx配置, 或者是K8S ingress规则配置有误,排查了很久。配置了反复调整location, 未见生效。

怀疑问题二:浏览器缓存,路由配置有误,站点A也存在同名/basic的路由

突发奇想想看看当访问zeeklog.com/B 时, 浏览器到底做了什么。

打开控制台发现但访问站点B时, 请求连接是: zeeklog.com/basic, 状态码为: 200(from service worker), 如醍醐灌顶, 出问题的一定是这个“(from service worker)”。

经过排查, 发现站点A中存在下述代码:

import { VitePWA } from 'vite-plugin-pwa'

深入解析

vite-plugin-pwa 是一个集成了 PWA (Progressive Web Application) 功能的 Vite 插件。PWA 允许网页通过使用 service worker 来提供类似于原生应用的体验,包括离线支持、推送通知等。
当你看到状态码 “200 OK (from service worker)” 的时候,意味着 service worker 正在介入处理网络请求。如果你通过 vite-plugin-pwa 导入了 VitePWA 并在你的 Vite 项目中配置了这个插件,它会生成 service worker 的代码来处理你网站的缓存策略。

问题逐渐清晰, 原站点A使用了service worker, 当在同一个TAB键入同域下内容是, 会优先被service worker处理。

AI介绍:

Service Worker 是一种在现代浏览器中运行的脚本,它作为Web应用与浏览器及网络(如果可用)之间的代理服务器。它设计用于在后台运行,独立于网页,使开发者能够在用户与应用间做出更丰富的交互,例如在网页脱离网络的情况下还能够工作,拦截网络请求,管理缓存的内容,以及在网络重新可用时发送推送消息。
Service Worker 的主要特性和功能包括:

  1. 离线体验:Service Worker 允许开发者控制文件缓存,这意味着在断网的情况下,用户能够访问到之前访问过的内容。
  2. 网络请求的拦截与处理:Service Worker 可以拦截来自你的应用发出的所有网络请求,并决定如何响应这些请求,可能是从缓存中提供内容,从网络获取新内容,或者结合上述两种方式。
  3. 背景同步:Service Worker 能在后台处理同步任务,这样即使应用关闭后,一旦网络恢复,应用还可以继续之前的操作并同步数据。
  4. 推送通知:开发者可以通过 Service Worker 发送推送通知,即使Web应用没有打开。
  5. 更新策略:Service Worker 提供了控制内容更新的策略,以确保用户始终访问到最新版本的应用。
  6. 安全性:由于 Service Worker 可以拦截网络请求和修改响应,出于安全考虑,它仅在通过 HTTPS 提供的网页上工作(localhost 除外,以便于开发)。

Service Worker 的一生:

  • 注册:在网页上,使用JavaScript注册Service Worker。
  • 安装:在注册之后,Service Worker 将触发安装事件,在这个阶段,可以缓存某些资产。
  • 激活:安装之后将触发激活事件,在此步骤可以管理旧缓存。
  • 运行:当Service Worker激活后,它将能够控制其作用域内的所有页面,并拦截请求。

Service Worker 运行在与网页JavaScript主线程分离的独立工作线程中,这意味着它不会阻塞页面执行或延迟页面响应用户操作,从而提升性能和用户体验。然而,Service Worker 的生命周期与网页独立,它不能直接访问DOM,并且仅通过消息传递与网页通信。
需要注意的是,Service Worker 由事件驱动,它不保证一直在后台运行,浏览器可以随时停止它以节省资源。再次需要时,浏览器会重启Service Worker。