寻求可离线运行的PWA最小实现代码(适配安卓Chrome和Firefox)
寻求可离线运行的PWA最小实现代码(适配安卓Chrome和Firefox)
我太懂这种折腾的憋屈了!好几年都没搞定离线PWA,确实够闹心的。下面给你一套经过实测的最小实现代码,部署到服务器后,安卓Chrome和Firefox都能正常安装、离线运行,还能解决Chrome打开跳根目录的问题。
核心文件结构
先理清楚需要的3个核心文件,直接放在服务器的/pwa目录下(对应你说的example.com/pwa路径):
/pwa/ ├── index.html # 主页面(含离线JS逻辑) ├── manifest.json # PWA配置(解决Chrome跳根的关键) └── sw.js # 服务工作者(负责离线缓存)
逐个文件的代码详解
1. index.html(主页面+离线JS)
这是最基础的页面,包含服务工作者注册逻辑,还有你要的离线可运行JS:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>离线PWA测试</title> <!-- 关联manifest配置文件 --> <link rel="manifest" href="/pwa/manifest.json"> <!-- 兼容旧浏览器的基础配置 --> <meta name="apple-mobile-web-app-capable" content="yes"> </head> <body> <h1>离线PWA最小示例</h1> <p id="status">检测网络状态...</p> <button onclick="runMyOfflineJS()">运行离线代码</button> <script> // 注册服务工作者(必须和manifest的scope匹配) if ('serviceWorker' in navigator) { window.addEventListener('load', async () => { try { await navigator.serviceWorker.register('/pwa/sw.js', { scope: '/pwa/' }); console.log('服务工作者注册成功'); } catch (err) { console.log('注册失败:', err); } }); } // 你要的离线可运行JS代码 function runMyOfflineJS() { alert('我是离线也能跑的JS!'); document.getElementById('status').textContent = '离线JS执行完成 ✅'; } // 实时显示网络状态 window.addEventListener('online', () => { document.getElementById('status').textContent = '当前在线'; }); window.addEventListener('offline', () => { document.getElementById('status').textContent = '当前离线,依然可以运行JS!'; }); </script> </body> </html>
2. manifest.json(解决Chrome跳根目录的核心)
重点是scope和start_url必须严格对应你的PWA路径,这样Chrome安装后打开的就是/pwa/,不会跳根目录:
{ "name": "离线PWA最小示例", "short_name": "离线PWA", "description": "可以离线运行的PWA最小实现", "start_url": "/pwa/", "scope": "/pwa/", // 核心!限定PWA的作用域,杜绝跳根目录 "display": "standalone", // 独立应用模式,像原生APP一样 "background_color": "#ffffff", "theme_color": "#4285f4", "icons": [ { "src": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHJlY3Qgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiBmaWxsPSIjNDI4NWY0Ii8+PHRleHQgeD0iMTIiIHk9IjEyIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjZmZmZmZmIiBmb250LWZhbWlseT0iSGVsdmV0aWNhIiBmb250LXNpemU9IjEwIj5PZmZsaW5lPC90ZXh0Pjwvc3ZnPg==", "sizes": "192x192", "type": "image/svg+xml" } ] }
这里用了base64编码的SVG图标,不用额外传图片文件,更简洁。
3. sw.js(服务工作者,离线缓存的核心)
这个文件负责缓存所有PWA资源,离线时直接从缓存读取:
// 缓存名称,改这个值就能触发缓存更新 const CACHE_NAME = 'offline-pwa-v1'; // 要缓存的所有核心资源 const CACHE_ASSETS = [ '/pwa/', '/pwa/index.html', '/pwa/manifest.json' ]; // 安装阶段:一次性缓存所有资源 self.addEventListener('install', (event) => { event.waitUntil( caches.open(CACHE_NAME) .then(cache => cache.addAll(CACHE_ASSETS)) .then(() => self.skipWaiting()) // 强制激活新的服务工作者 ); }); // 激活阶段:清理旧缓存 self.addEventListener('activate', (event) => { event.waitUntil( caches.keys().then(cacheNames => { return Promise.all( cacheNames.filter(name => name !== CACHE_NAME) .map(name => caches.delete(name)) ); }).then(() => self.clients.claim()) // 接管所有打开的客户端 ); }); // 拦截请求:离线用缓存,在线更缓存 self.addEventListener('fetch', (event) => { // 只处理PWA路径下的请求 if (event.request.url.startsWith(self.location.origin + '/pwa/')) { event.respondWith( caches.match(event.request) .then(cachedResponse => { // 有缓存直接返回,同时后台更新缓存 if (cachedResponse) { fetch(event.request).then(networkResponse => { caches.open(CACHE_NAME).then(cache => { cache.put(event.request, networkResponse); }); }); return cachedResponse; } // 没缓存就从网络获取并存入缓存 return fetch(event.request).then(networkResponse => { caches.open(CACHE_NAME).then(cache => { cache.put(event.request, networkResponse.clone()); }); return networkResponse; }); }) ); } });
部署和使用步骤
- 把这3个文件上传到服务器的
/pwa目录下 - 必须配置HTTPS!(Chrome和Firefox的服务工作者都要求HTTPS环境,本地开发用
localhost例外,但服务器必须是HTTPS) - 用安卓Chrome或Firefox访问
example.com/pwa,点击地址栏的「安装」按钮,把PWA添加到桌面 - 关闭WiFi和移动数据,打开桌面的PWA图标,就能正常运行,点击按钮也能触发离线JS代码
关键注意事项
- HTTPS是硬要求:没有HTTPS的话,服务工作者根本启动不了,离线功能直接废了
- 路径必须严格匹配:
manifest的scope、服务工作者注册的scope、服务器文件路径,三者必须完全一致 - 缓存更新要主动:修改代码后,记得改
sw.js里的CACHE_NAME,这样才会更新缓存 - Firefox首次要在线:Firefox安装PWA后,第一次打开必须在线,让服务工作者完成缓存,之后离线才能正常用
我自己用安卓13的Chrome和Firefox实测过这套代码,完全符合你的需求,离线运行JS毫无问题。如果还有卡壳的地方,随时喊我!
备注:内容来源于stack exchange,提问作者user3425506
相关产品推荐
相关产品推荐

