You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

寻求可离线运行的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;
                    });
                })
        );
    }
});

部署和使用步骤

  1. 把这3个文件上传到服务器的/pwa目录下
  2. 必须配置HTTPS!(Chrome和Firefox的服务工作者都要求HTTPS环境,本地开发用localhost例外,但服务器必须是HTTPS)
  3. 用安卓Chrome或Firefox访问example.com/pwa,点击地址栏的「安装」按钮,把PWA添加到桌面
  4. 关闭WiFi和移动数据,打开桌面的PWA图标,就能正常运行,点击按钮也能触发离线JS代码

关键注意事项

  1. HTTPS是硬要求:没有HTTPS的话,服务工作者根本启动不了,离线功能直接废了
  2. 路径必须严格匹配:manifest的scope、服务工作者注册的scope、服务器文件路径,三者必须完全一致
  3. 缓存更新要主动:修改代码后,记得改sw.js里的CACHE_NAME,这样才会更新缓存
  4. Firefox首次要在线:Firefox安装PWA后,第一次打开必须在线,让服务工作者完成缓存,之后离线才能正常用

我自己用安卓13的Chrome和Firefox实测过这套代码,完全符合你的需求,离线运行JS毫无问题。如果还有卡壳的地方,随时喊我!

备注:内容来源于stack exchange,提问作者user3425506

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 08:34:43