H5 添加到手机桌面快捷方式 注意一定要https 协议才可以添加

 icon   

index,html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>添加到桌面</title>

<!-- iOS 支持 -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="我的应用">
<link rel="apple-touch-icon" href="icon.svg">

<!-- Android Chrome 支持 -->
<link rel="manifest" href="manifest.json">
<meta name="theme-color" content="#4285f4">

<style>
    * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
    }
    body {
        font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
        padding: 20px;
        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
        min-height: 100vh;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
    }
    .container {
        background: white;
        border-radius: 16px;
        padding: 30px;
        box-shadow: 0 10px 40px rgba(0,0,0,0.2);
        max-width: 400px;
        width: 100%;
    }
    h1 {
        font-size: 24px;
        color: #333;
        margin-bottom: 20px;
        text-align: center;
    }
    .btn {
        width: 100%;
        padding: 16px;
        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
        color: white;
        border: none;
        border-radius: 8px;
        font-size: 16px;
        font-weight: 600;
        cursor: pointer;
        transition: transform 0.2s;
        margin-bottom: 15px;
    }
    .btn:active {
        transform: scale(0.98);
    }
    .guide {
        display: none;
        background: #f8f9fa;
        border-radius: 8px;
        padding: 20px;
        margin-top: 20px;
    }
    .guide.show {
        display: block;
    }
    .guide h3 {
        font-size: 18px;
        color: #333;
        margin-bottom: 15px;
    }
    .guide ol {
        padding-left: 20px;
        color: #666;
        line-height: 1.8;
    }
    .guide li {
        margin-bottom: 8px;
    }
    .icon {
        font-size: 48px;
        text-align: center;
        margin-bottom: 20px;
    }
    .tip {
        font-size: 14px;
        color: #666;
        text-align: center;
        margin-top: 15px;
        line-height: 1.6;
    }
</style>
</head>
<body>
<div class="container">
    <div class="icon">📱</div>
    <h1>添加到桌面</h1>
    <button class="btn" onclick="addToHomeScreen()">添加快捷方式</button>

    <div id="iosGuide" class="guide">
        <h3>iOS 添加步骤:</h3>
        <ol>
            <li>点击底部的 <strong>分享</strong> 按钮 📤</li>
            <li>向下滚动找到 <strong>"添加到主屏幕"</strong></li>
            <li>点击 <strong>"添加"</strong> 完成</li>
        </ol>
    </div>

    <div id="androidGuide" class="guide">
        <h3>Android 添加步骤:</h3>
        <ol>
            <li>点击浏览器右上角 <strong>菜单</strong> ⋮</li>
            <li>选择 <strong>"添加到主屏幕"</strong></li>
            <li>确认添加即可</li>
        </ol>
    </div>

    <p class="tip" id="tipText">点击按钮将本页面添加到手机桌面</p>

    <!-- 调试信息 -->
    <div style="margin-top: 20px; padding: 15px; background: #f0f0f0; border-radius: 8px; font-size: 12px; color: #666;">
        <div><strong>调试信息:</strong></div>
        <div id="debugInfo"></div>
    </div>
</div>

<script>
let deferredPrompt;
let isIOS = false;
let isAndroid = false;

// 检测设备类型
function detectDevice() {
    const ua = navigator.userAgent.toLowerCase();
    isIOS = /iphone|ipad|ipod/.test(ua);
    isAndroid = /android/.test(ua);

    console.log('设备检测:', { isIOS, isAndroid, ua });
}

// 监听 PWA 安装提示事件(Android Chrome)
window.addEventListener('beforeinstallprompt', (e) => {
    console.log('beforeinstallprompt 事件触发');
    e.preventDefault();
    deferredPrompt = e;
    document.getElementById('tipText').textContent = '点击按钮即可添加到桌面';
});

// 监听安装成功事件
window.addEventListener('appinstalled', () => {
    console.log('PWA 安装成功');
    deferredPrompt = null;
    alert('添加成功!请在桌面查看');
});

// 添加到主屏幕
async function addToHomeScreen() {
    detectDevice();

    // Android Chrome - PWA 方式
    if (deferredPrompt) {
        console.log('使用 PWA 安装提示');
        try {
            deferredPrompt.prompt();
            const { outcome } = await deferredPrompt.userChoice;
            console.log('用户选择:', outcome);

            if (outcome === 'accepted') {
                alert('添加成功!');
            } else {
                showAndroidGuide();
            }
            deferredPrompt = null;
        } catch (err) {
            console.error('PWA 安装失败:', err);
            showAndroidGuide();
        }
    }
    // iOS Safari
    else if (isIOS) {
        console.log('iOS 设备,显示引导');
        showIOSGuide();
    }
    // Android 其他浏览器
    else if (isAndroid) {
        console.log('Android 设备,显示引导');
        showAndroidGuide();
    }
    // 其他情况
    else {
        alert('请在手机浏览器中打开此页面');
    }
}

// 显示 iOS 引导
function showIOSGuide() {
    const guide = document.getElementById('iosGuide');
    guide.classList.add('show');
    document.getElementById('androidGuide').classList.remove('show');
    document.getElementById('tipText').textContent = '请按照以下步骤操作';
}

// 显示 Android 引导
function showAndroidGuide() {
    const guide = document.getElementById('androidGuide');
    guide.classList.add('show');
    document.getElementById('iosGuide').classList.remove('show');
    document.getElementById('tipText').textContent = '请按照以下步骤操作';
}

// 注册 Service Worker
if ('serviceWorker' in navigator) {
    window.addEventListener('load', () => {
        navigator.serviceWorker.register('./sw.js')
            .then((registration) => {
                console.log('Service Worker 注册成功:', registration.scope);
            })
            .catch((error) => {
                console.log('Service Worker 注册失败:', error);
            });
    });
}

// 显示调试信息
function showDebugInfo() {
    const debugInfo = document.getElementById('debugInfo');
    const isHTTPS = location.protocol === 'https:';
    const hasSW = 'serviceWorker' in navigator;
    const isStandalone = window.navigator.standalone || window.matchMedia('(display-mode: standalone)').matches;

    debugInfo.innerHTML = `
        <div>协议: ${location.protocol} ${isHTTPS ? '✅' : '❌ 需要 HTTPS'}</div>
        <div>支持 Service Worker: ${hasSW ? '✅' : '❌'}</div>
        <div>已安装: ${isStandalone ? '是' : '否'}</div>
        <div>PWA 提示: ${deferredPrompt ? '✅ 已捕获' : '❌ 未触发'}</div>
        <div>浏览器: ${navigator.userAgent}</div>
    `;
}

// 页面加载时检测
window.addEventListener('load', () => {
    detectDevice();

    // 延迟显示调试信息,等待 beforeinstallprompt 事件
    setTimeout(showDebugInfo, 1000);

    // 检测是否已经在独立模式运行(已添加到桌面)
    if (window.navigator.standalone || window.matchMedia('(display-mode: standalone)').matches) {
        document.getElementById('tipText').textContent = '已添加到桌面,正在独立模式运行';
        document.querySelector('.btn').disabled = true;
        document.querySelector('.btn').textContent = '已添加到桌面';
        document.querySelector('.btn').style.opacity = '0.6';
    }
});
</script>
</body>
</html>
sw.js
// Service Worker for PWA
const CACHE_NAME = 'app-cache-v1';
const urlsToCache = [
  './Untitled-3.html',
  './manifest.json',
  './icon.svg'
];

// 安装 Service Worker
self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then((cache) => cache.addAll(urlsToCache))
  );
});

// 激活 Service Worker
self.addEventListener('activate', (event) => {
  event.waitUntil(
    caches.keys().then((cacheNames) => {
      return Promise.all(
        cacheNames.map((cacheName) => {
          if (cacheName !== CACHE_NAME) {
            return caches.delete(cacheName);
          }
        })
      );
    })
  );
});

// 拦截请求
self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request)
      .then((response) => response || fetch(event.request))
  );
});
manifest.json
{
  "name": "我的应用",
  "short_name": "应用",
  "description": "添加到桌面的快捷方式",
  "start_url": "./Untitled-3.html",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#4285f4",
  "orientation": "portrait",
  "icons": [
    {
      "src": "icon.svg",
      "sizes": "any",
      "type": "image/svg+xml",
      "purpose": "any maskable"
    },
    {
      "src": "icon.svg",
      "sizes": "192x192",
      "type": "image/svg+xml"
    },
    {
      "src": "icon.svg",
      "sizes": "512x512",
      "type": "image/svg+xml"
    }
  ]
}
部署nginx 测试
# nginx 配置示例
server {
    listen 80;
    server_name your-domain.com;

    # 强制跳转到 HTTPS(重要!)
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name your-domain.com;

    # SSL 证书配置
    ssl_certificate /path/to/your/cert.pem;
    ssl_certificate_key /path/to/your/key.pem;

    root /path/to/your/files;
    index index.html;

    # 重要:设置正确的 MIME 类型
    location ~ \.json$ {
        add_header Content-Type application/json;
        add_header Cache-Control "public, max-age=0";
    }

    location ~ \.js$ {
        add_header Content-Type application/javascript;
        add_header Cache-Control "public, max-age=0";
    }

    location ~ \.svg$ {
        add_header Content-Type image/svg+xml;
        add_header Cache-Control "public, max-age=31536000";
    }

    # Service Worker 不能被缓存
    location = /sw.js {
        add_header Content-Type application/javascript;
        add_header Cache-Control "no-cache, no-store, must-revalidate";
        add_header Service-Worker-Allowed "/";
    }

    # manifest.json
    location = /manifest.json {
        add_header Content-Type application/manifest+json;
        add_header Cache-Control "public, max-age=0";
    }
}
posted @ 2026-01-30 09:18  前端搬运工bug  阅读(0)  评论(0)    收藏  举报