当一个 PWA 被安装后,Service Worker 就会在后台运行,成为应用的一部分,检测 Service Worker 是否处于 "activated"(激活)状态,是判断 PWA 是否被安装的最可靠方法。

js实现网页检测是否安装了
(图片来源网络,侵删)

下面我将分步详细解释如何实现,并提供完整的代码示例。

核心原理

  1. Service Worker (SW):PWA 的核心技术,它是一个在浏览器后台运行的脚本,可以处理网络请求、实现离线缓存、推送通知等,当一个 PWA 被安装后,它的 Service Worker 就会被注册并激活。
  2. 检测方法:我们可以通过 navigator.serviceWorker API 来查询当前活动的 Service Worker。
    • 如果存在一个已注册且处于激活状态的 Service Worker,那么就可以认为该 PWA 已经被安装。
    • 如果不存在,则说明 PWA 还未被安装。

实现步骤与代码

我们将创建一个简单的网页,它有两个按钮:“安装应用”和“检测安装状态”,并实时显示当前状态。

第 1 步:创建 PWA 清单文件 (manifest.json)

这是 PWA 的“身份证”,定义了应用的名称、图标、启动画面等,并告诉浏览器这是一个可安装的应用。

创建一个 manifest.json 文件,内容如下:

js实现网页检测是否安装了
(图片来源网络,侵删)
{
  "name": "我的第一个 PWA",
  "short_name": "MyPWA",
  "description": "一个演示 PWA 安装检测的示例应用。",
  "start_url": ".",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#3367d6",
  "icons": [
    {
      "src": "icon-192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "icon-512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ]
}

注意:你需要在项目根目录下提供 icon-192.pngicon-512.png 这两个图标文件。

第 2 步:注册 Service Worker (通常在 index.js 或主 HTML 文件中)

Service Worker 的注册代码通常放在你的主 JavaScript 文件中。

// 在你的主 JS 文件中 (app.js)
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/sw.js')
      .then(registration => {
        console.log('ServiceWorker 注册成功: ', registration.scope);
      })
      .catch(err => {
        console.log('ServiceWorker 注册失败: ', err);
      });
  });
}

第 3 步:编写 Service Worker 文件 (sw.js)

这个文件负责缓存资源,是实现 PWA 离线功能的关键,即使我们不实现复杂的缓存逻辑,也需要一个空的 sw.js 文件来让注册成功。

创建一个 sw.js 文件,可以简单到只包含一个 'use strict';

js实现网页检测是否安装了
(图片来源网络,侵删)
// sw.js
'use strict';
// 在这里可以添加缓存逻辑,
// self.addEventListener('install', event => {
//   event.waitUntil(
//     caches.open('my-cache-v1').then(cache => {
//       return cache.addAll([
//         '/',
//         '/index.html',
//         '/style.css',
//         // ... 其他需要缓存的文件
//       ]);
//     })
//   );
// });

第 4 步:编写检测安装状态的 JavaScript 逻辑

这是核心部分,我们将创建一个函数来检测状态,并根据结果更新 UI。

// 在你的主 JS 文件中 (app.js)
const checkInstallationButton = document.getElementById('check-install');
const installButton = document.getElementById('install-btn');
const statusDisplay = document.getElementById('status');
// --- 核心检测函数 ---
function isPwaInstalled() {
  // 检查是否存在已激活的 Service Worker
  return !!navigator.serviceWorker && 
         navigator.serviceWorker.controller !== null;
}
// --- 更新UI状态的函数 ---
function updateUI() {
  if (isPwaInstalled()) {
    statusDisplay.textContent = '✅ PWA 已安装';
    statusDisplay.style.color = 'green';
    installButton.style.display = 'none'; // 如果已安装,隐藏安装按钮
  } else {
    statusDisplay.textContent = '❌ PWA 未安装';
    statusDisplay.style.color = 'red';
    installButton.style.display = 'inline-block'; // 如果未安装,显示安装按钮
  }
}
// --- 检测按钮点击事件 ---
checkInstallationButton.addEventListener('click', () => {
  updateUI();
});
// --- 安装按钮点击事件 (处理浏览器的安装提示) ---
installButton.addEventListener('beforeinstallprompt', (e) => {
  // 阻止浏览器默认的安装提示
  e.preventDefault();
  // 保存事件,以便稍后手动触发
  deferredPrompt = e;
  // 显示我们的安装按钮
  installButton.style.display = 'block';
  installButton.textContent = '安装应用';
});
let deferredPrompt = null;
installButton.addEventListener('click', () => {
  if (deferredPrompt) {
    // 显示浏览器安装提示
    deferredPrompt.prompt();
    // 等待用户的选择
    deferredPrompt.userChoice.then((choiceResult) => {
      if (choiceResult.outcome === 'accepted') {
        console.log('用户接受了安装提示');
      } else {
        console.log('用户拒绝了安装提示');
      }
      // 重置 deferredPrompt
      deferredPrompt = null;
    });
  }
});
// --- 监听 Service Worker 的变化 ---
navigator.serviceWorker.addEventListener('controllerchange', () => {
  console.log('Service Worker 已更新');
  updateUI(); // 当 SW 变化时,重新检测状态
});
// 页面加载时,先检测一次状态
window.addEventListener('load', () => {
  updateUI();
});

第 5 步:编写 HTML 文件

将所有部分组合起来。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">PWA 安装检测示例</title>
  <link rel="manifest" href="manifest.json">
  <!-- 关键:让浏览器知道这是一个 PWA -->
  <meta name="apple-mobile-web-app-capable" content="yes">
  <style>
    body { font-family: sans-serif; text-align: center; padding-top: 50px; }
    button { padding: 10px 20px; font-size: 16px; cursor: pointer; margin: 10px; }
    #status { font-size: 18px; font-weight: bold; margin-top: 20px; }
  </style>
</head>
<body>
  <h1>我的第一个 PWA</h1>
  <p>这是一个演示如何检测 PWA 是否已安装的网页。</p>
  <button id="check-install">检测安装状态</button>
  <button id="install-btn" style="display: none;">安装应用</button>
  <div id="status">正在检测...</div>
  <!-- 引入主 JS 文件 -->
  <script src="app.js"></script>
</body>
</html>

总结与注意事项

  1. HTTPS 是必须的:Service Worker 和 PWA 功能必须在 https 协议下才能正常工作(在 localhost 上开发除外)。
  2. beforeinstallprompt 事件:这是浏览器在认为你的应用“可安装”时触发的事件,你需要监听它,并阻止默认行为,然后保存事件对象 (deferredPrompt),以便在用户点击你的自定义安装按钮时手动调用 prompt() 方法。
  3. 用户体验
    • 不要滥用:不要频繁弹窗提示用户安装,这会非常烦人,通常只在用户多次访问或在特定操作后提示。
    • 提供入口:提供一个明确的按钮(如“安装到主屏幕”)让用户主动发起安装流程。
    • 提供反馈:明确告诉用户当前是“已安装”还是“未安装”状态。
  4. Safari 的特殊性:Safari 对 PWA 的支持相对较弱,其安装体验和检测逻辑可能与 Chrome/Edge 等浏览器有所不同,上述代码在现代浏览器中是通用的,但可能需要针对 Safari 进行微调。

通过以上步骤,你就可以成功地在一个网页中实现检测 PWA 是否已安装的功能了。