尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Front-End-Checklist 之 offline-fallback:基于 Service Worker 实现自定义离线回退页的完整指南

Front-End-Checklist 之 offline-fallback:基于 Service Worker 实现自定义离线回退页的完整指南 Front-End-Checklist 之 offline-fallback基于 Service Worker 实现自定义离线回退页的完整指南【免费下载链接】Front-End-Checklist The essential checklist for modern web development, for humans and AI agents项目地址: https://gitcode.com/gh_mirrors/fr/Front-End-Checklist在弱网或完全断网的场景下浏览器只会显示一张与你的产品毫无关系的默认错误页。本文基于 Front-End-Checklist 仓库中的offline-fallback技能文档SKILL.md及其完整规则说明references/rule.md系统讲解如何创建自包含的离线回退页、在 Service Worker 的install事件中预缓存、在fetch处理器中拦截失败导航请求并返回缓存页面同时给出 Workbox 方案、在线/离线状态横幅以及可直接执行的验证清单。读完本文你可以为任意站点落地一套可被 Lighthouse PWA 审计识别的离线体验。规则定位这条规则在 Checklist 中的坐标offline-fallback是 Front-End-Checklist 规则库中performance/loading子分类下的一条规则其元数据定义在 offline-fallback.mdx 的 frontmatter 中分类performance子分类loading优先级low难度beginner预估耗时20 分钟AI 适用场景aiContext为站点添加 PWA 能力、实现 Service Worker、或改进弱网用户体验时使用该文档同时面向 LLM Agent 提供了四个标准提示prompts 字段也是理解这条规则工程闭环的钥匙提示类型内容check检查断网且导航请求无法被满足时站点是否展示自定义离线回退页fix创建自包含的/offline页面并配置 Service Worker 预缓存它、在导航失败时返回它explain解释 Service Worker 如何拦截失败的请求并返回缓存的离线页codeReview审查 Service Worker 的 fetch 处理器与离线页标记确认离线页在 install 时已预缓存、不依赖外部资源且回退只作用于导航请求而非子资源仓库中与本规则直接关联的是 service-worker.mdxService Worker 正是缓存并返回离线回退页的机制两条规则被标注为“共同实现”。本文的后半部分将结合该规则的实现细节展开。为什么需要自定义离线回退页浏览器默认的离线错误屏“No internet connection”令人困惑且完全脱离品牌。自定义离线页的价值在于维持用户体验连续性用户停留在你的产品语境中而不是被抛到一张系统级错误页强化信任产品主动处理异常传递出“可控”的信号承载可用动作展示已缓存内容或引导用户继续阅读一篇已缓存的文章、将一次表单提交排队等待网络恢复后再发送。离线页必须满足的四条硬约束不发起任何网络请求—— 所有 CSS、JavaScript、图片必须内联或已被缓存明确告知用户—— 清楚地说明“你已离线”提供可操作选项—— 重试按钮、已缓存页面列表、或导航到已缓存内容的入口贴合品牌—— 一致的 Logo、字体预缓存字体或系统字体与配色。最后一点常被忽视离线页引用的任何外部资源字体、图片、脚本本身都必须被预缓存或内联。如果离线页发起的请求失败浏览器可能显示一片空白或破损页面——这比浏览器原生错误屏更糟。第一步创建自包含的 /offline 页面把离线页做成一个零依赖的 HTML 文件例如放在public/offline.html。以下是规则文档给出的完整参考实现原文见 rule.md 的 Code Example 一节!-- public/offline.html -- !DOCTYPE html html langen head meta charsetUTF-8 / meta nameviewport contentwidthdevice-width, initial-scale1.0 / titleYoure offline — Acme App/title style /* All styles must be inline — no external stylesheets */ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: system-ui, -apple-system, sans-serif; background: #f8fafc; color: #1e293b; display: flex; align-items: center; justify-content: center; min-height: 100vh; padding: 1.5rem; } .card { background: #fff; border: 1px solid #e2e8f0; border-radius: 1rem; padding: 2.5rem 2rem; max-width: 420px; width: 100%; text-align: center; } .icon { font-size: 3rem; margin-bottom: 1rem; } h1 { font-size: 1.5rem; font-weight: 700; margin-bottom: 0.5rem; } p { color: #64748b; line-height: 1.6; margin-bottom: 1.5rem; } button { background: #3b82f6; color: #fff; border: none; border-radius: 0.5rem; padding: 0.625rem 1.5rem; font-size: 0.95rem; cursor: pointer; transition: background 0.15s; } button:hover { background: #2563eb; } button:active { background: #1d4ed8; } /style /head body div classcard div classicon aria-hiddentrue/div h1Youre offline/h1 p It looks like youve lost your internet connection. Check your network settings and try again. /p button onclickwindow.location.reload()Try again/button /div /body /html这份示例体现了上文四条硬约束的典型落法样式全部写在style内联块中、字体使用system-ui系统字体栈避免字体文件请求、装饰图标用aria-hiddentrue对屏幕阅读器隐藏以及一个直接调用window.location.reload()的重试按钮——重试逻辑不需要任何外部脚本。第二步在 install 事件中预缓存离线页离线页只有被缓存后离线时才能被caches.match()找到。因此要把/offline或/offline.html加入 Service Workerinstall事件的预缓存列表原文见 rule.md 的 Step 2// public/sw.js const CACHE_NAME static-v1 const PRECACHE_URLS [ /, /offline, // ← the offline fallback page /styles/main.css, /scripts/app.js, ] self.addEventListener(install, (event) { event.waitUntil( caches .open(CACHE_NAME) .then((cache) cache.addAll(PRECACHE_URLS)) .then(() self.skipWaiting()) ) })几个实现要点event.waitUntil确保 Worker 安装流程会等待addAll完成addAll中任何一个 URL 失败都会使整个安装失败所以预缓存列表里的每一项都必须稳定可访问self.skipWaiting()让新版本 Worker 立即进入等待激活状态避免用户必须关闭所有标签页才能拿到新缓存缓存名static-v1即缓存版本。这一点在仓库的 service-worker 规则 中有专门展开部署破坏性变更时应提升CACHE_VERSION否则用户可能收到新旧文件混用的结果并且给出用构建时间戳自动生成版本的示例process.env.BUILD_ID ?? Date.now().toString()。第三步导航失败时返回离线页在fetch处理器中捕获导航请求的网络错误并返回缓存中的离线页原文见 rule.md 的 Step 3// public/sw.js (fetch handler) self.addEventListener(fetch, (event) { // Only handle same-origin GET requests if ( event.request.method ! GET || !event.request.url.startsWith(self.location.origin) ) { return } if (event.request.mode navigate) { event.respondWith(handleNavigationRequest(event.request)) } }) async function handleNavigationRequest(request) { try { // Always try the network first for navigation const networkResponse await fetch(request) // Cache a copy for later const cache await caches.open(CACHE_NAME) cache.put(request, networkResponse.clone()) return networkResponse } catch { // Network failed — serve cached page if available, otherwise offline page const cached await caches.match(request) if (cached) return cached const offlinePage await caches.match(/offline) return ( offlinePage ?? new Response(h1Offline/h1, { headers: { Content-Type: text/html }, }) ) } }这段逻辑的决策顺序值得逐层拆解只处理同源 GET非 GET 请求如 POST 提交与跨域请求直接放行避免把离线逻辑套到不该套的请求上只拦截导航请求event.request.mode navigate是导航的判据。这也正是codeReview提示的要求——回退只服务于导航请求而不是图片、字体等子资源导航采用网络优先导航请求总是先尝试网络成功后顺手把响应clone()一份写入缓存供下次离线使用注意clone()的必要性Response的正文流只能消费一次三级降级网络失败后先找该 URL 的历史缓存用户之前访问过的页面找不到再返回/offline页终极兜底如果连/offline都不在缓存中比如 Worker 安装失败仍返回一个最简的h1Offline/h1文本响应保证用户永远不会看到白屏。与 service-worker 规则的完整实现对照仓库中 service-worker 规则 给出了同一机制的完整版实现可以和本节的三个片段拼合成一个生产级 Service Worker注册L102-L137在主入口尽早调用navigator.serviceWorker.register(/sw.js, { scope: / })先用serviceWorker in navigator做能力探测并在updatefound事件中提示用户有新版本待刷新注册放在页面load之后避免与关键资源竞争带宽install / activate 生命周期L157-L183activate事件会删除不在白名单内的旧缓存即上一版本命名的缓存并调用clients.claim()这是离线页“更新即生效”的前提按资源类型分策略L186-L209静态资源走cacheFirst导航走networkFirstWithOfflineFallback动态 API 走staleWhileRevalidate其中导航策略的实现L231-L248与本文第三步的handleNavigationRequest是同一套“网络 → 该页缓存 →/offline”的三级降级安全边界L343-L345不要缓存 POST 请求或带鉴权的 API 响应缓存范围应限定在幂等的、非敏感的 GET 请求内。增强页面上的在线/离线状态横幅离线回退页只覆盖“断网后发起新导航”的场景。对于已经加载出来的页面可以用online/offline窗口事件做一个轻量的连接状态提示原文见 rule.md 的 Detecting Online/Offline State in the UI// Notify users when connection is lost or restored function setupConnectivityBanner() { const banner document.createElement(div) banner.setAttribute(role, status) banner.setAttribute(aria-live, polite) banner.style.cssText position: fixed; bottom: 1rem; left: 50%; transform: translateX(-50%); background: #1e293b; color: #fff; padding: 0.5rem 1.25rem; border-radius: 2rem; font-size: 0.875rem; display: none; z-index: 9999; document.body.appendChild(banner) function showBanner(message: string) { banner.textContent message banner.style.display block } function hideBanner() { banner.style.display none } window.addEventListener(offline, () showBanner(You are offline)) window.addEventListener(online, () { showBanner(Back online) setTimeout(hideBanner, 3000) }) } if (typeof window ! undefined) { setupConnectivityBanner() }实现上有两个细节值得保留横幅使用rolestatus与aria-livepolite让屏幕阅读器在连接状态变化时自动播报typeof window ! undefined守卫使同一段代码可以安全地放在会被服务端渲染的执行路径中。Workbox 方案setCatchHandler 自动兜底如果使用 WorkboxofflineFallback相关的样板逻辑可以被precacheAndRoute加setCatchHandler的组合替代原文见 rule.md 的 Using Workbox// sw.ts declare const self: ServiceWorkerGlobalScope { __WB_MANIFEST: unknown[] } precacheAndRoute(self.__WB_MANIFEST) // /offline must be in the manifest registerRoute( ({ request }) request.mode navigate, new NetworkFirst({ cacheName: pages }) ) // Catch all failed navigation requests setCatchHandler(async ({ request }) { if (request.destination document) { return (await caches.match(/offline))! } return Response.error() })要点/offline必须出现在构建工具注入的__WB_MANIFEST预缓存清单中否则caches.match(/offline)会取不到setCatchHandler是路由失败含网络不可达后的统一落点用request.destination document精确圈定“这是文档级导航”对非文档请求直接Response.error()避免把离线页 HTML 喂给图片、脚本等请求。仓库的 service-worker 规则 还展示了 Workbox 的完整生产形态静态资源走CacheFirst并挂ExpirationPlugin30 天过期、/api/走NetworkFirst、页面走StaleWhileRevalidate以及 Next.js 下通过next-pwa配置runtimeCaching的集成方式——离线回退页在这套体系中只需确保它进入预缓存清单即可。验证清单如何确认离线回退页真正生效规则文档将验证分为自动化与手动两组原文见 rule.md 的 Verification 一节均可直接照做自动化检查注册 Service Worker 后在 DevTools →Application→Service Workers中确认其处于激活状态在 Network 面板把网络状态切换为Offline然后访问一个不在缓存中的页面——应该看到你的自定义离线页而不是浏览器默认错误屏运行 Lighthouse PWA 审计确认 “Responds with a 200 when offline” 检查项通过。手动检查在Cache Storage面板中确认/offline出现在你的缓存名如static-v1之下。另有一条来自 Support Notes 的工程提醒离线行为依赖浏览器对 Service Worker、Cache Storage 与可安装性的实际支持应在受支持的多浏览器上验证而不是只在单一开发环境验证并显式记录对不支持浏览器的优雅降级方案。从仓库中 service-worker 规则的 Support Notes 看该类能力的基线兼容参考为 chrome 115、edge 115、firefox 116、safari 16.4、safari_ios 16.4当你的目标浏览器矩阵低于此范围时应额外提供降级说明。代码审查要点小结落地这条规则后可以用codeReview提示中的三个问题做最终把关离线页是否在install事件中通过addAll预缓存离线页是否真的零外部依赖内联样式、系统字体、无外部脚本/图片回退是否只对mode navigate或destination document的请求生效而不会把 HTML 返回给子资源请求这三点分别对应离线页“拿得到”“打得开”“不串味”三类最常见的线上事故。完整规则文案可回溯至 packages/content/rules/en/performance/offline-fallback.mdx配套的 Agent 技能入口为 skills/offline-fallback/SKILL.md其中声明了该技能的触发场景——“为站点添加 PWA 能力、实现 Service Worker、或改进不可靠网络下用户体验时使用”。【免费下载链接】Front-End-Checklist The essential checklist for modern web development, for humans and AI agents项目地址: https://gitcode.com/gh_mirrors/fr/Front-End-Checklist创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表