离线预览功能无法用 Service Worker 实现,因其仅控制同源真实 HTTP(S) 页面请求,而在线工具多使用 data:/blob: URL 或受限 iframe,导致 navigator.serviceWorker 未定义或注册失败;可行方案是用 localStorage 存储拼接的 HTML 字符串,并通过 iframe srcdoc 属性离线渲染。

离线预览功能在 HTML 在线运行工具(如 CodePen、JSFiddle 类型的编辑器)中无法直接用 Service Worker 实现——因为这类工具通常把用户代码注入 iframe 或通过 data: URL / blob: URL 执行,而 Service Worker 只能控制同源、已注册 scope 下的**真实 HTTP(S) 页面请求**。
为什么 navigator.serviceWorker.register() 在编辑器里大概率失败
在线 HTML 工具多数不提供真实服务器路径和 HTTPS 上下文:
- 用户代码常运行在
data:text/html,或blob:http://...协议下 →navigator.serviceWorker为undefined,注册直接报TypeError: Cannot read property 'register' of undefined - 即使工具用 iframe 加载
https://example.com/preview.html,其scope也极可能受限(比如只允许/preview/),且无法让用户上传或控制sw.js文件部署位置 - 编辑器自身 JS 通常在沙盒环境或不同 origin 下执行,无法在用户代码中调用
register()—— 浏览器会拒绝跨 origin 注册
替代方案:用 localStorage + iframe 模拟“离线预览”
真正可行的离线预览,不是靠 Service Worker 拦截网络,而是把用户当前编辑的 HTML/CSS/JS **序列化后存进 localStorage,再用 srcdoc 属性直接渲染**:
-
iframe支持srcdoc属性,可直接传入 HTML 字符串,无需网络请求,完全离线 - 把编辑器三栏内容拼成完整 HTML:
<style>${css}</style>${html}<script>${js}</script> - 每次保存时写入
localStorage.setItem('html-preview', htmlString),加载时读取并设到iframe.srcdoc - 注意转义:若 JS 中含
,需先替换为,否则会提前闭合标签
如果真要上 Service Worker,必须满足这 3 个硬条件
只有当你的“HTML 在线运行工具”本身是一个独立部署的 PWA(比如你自建的 https://my-editor.example.com),才可能启用 SW:
立即学习“前端免费学习笔记(深入)”;
- 整个工具页面必须通过 HTTPS 提供(
localhost可例外) -
sw.js必须放在根目录(如https://my-editor.example.com/sw.js),且注册时用navigator.serviceWorker.register('/sw.js') - 预览页必须是真实 URL(如
/preview?code=abc123),不能是data:或动态blob:;SW 才能在fetch事件中匹配该 URL 并返回缓存的渲染结果
caches.match() 对预览页无效的典型现象
即使你绕过注册问题强行装上了 SW,下面这些情况仍会让缓存失效:
- 预览 iframe 的
src是https://my-editor.example.com/preview?html=xxx&css=yyy→ 查询参数不同,每个 URL 都算独立键,缓存爆炸 - 用户改了一行 CSS,生成新 URL,但 SW 里没监听变化、也没触发
cache.put()更新对应 entry -
fetch事件中没过滤request.destination === 'iframe'或request.mode === 'navigate',导致把预览请求误判为 API 调用而放行到网络
真正落地的离线预览,99% 场景下应放弃 Service Worker,老实用 srcdoc + localStorage。Service Worker 的价值在于托管静态资源、管理版本、兜底导航——它不是用来运行用户即席代码的沙盒。


















