<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>我的 PWA 应用</title>
<link rel="manifest" href="/manifest.json">
<meta name="theme-color" content="#317EFB">
<link rel="apple-touch-icon" href="/icons/icon-192x192.png">
<link rel="preload" href="/style.css" as="style">
<link rel="preload" href="/script.js" as="script">
<link rel="stylesheet" href="/style.css">
</head>
<body>
<h1>我的 PWA 应用</h1>
<p>这是一个完整的 PWA 应用</p>
<button id="notification-btn">发送通知</button>
<script src="/script.js"></script>
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/service-worker.js')
.then(registration => {
console.log('Service Worker 注册成功:', registration.scope);
})
.catch(error => {
console.log('Service Worker 注册失败:', error);
});
});
}
</script>
</body>
</html>
{
"name": "我的 PWA 应用",
"short_name": "PWA 应用",
"description": "一个完整的 PWA 应用示例",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#317EFB",
"icons": [
{
"src": "/icons/icon-72x72.png",
"sizes": "72x72",
"type": "image/png",
"purpose": "any"
},
{
"src": "/icons/icon-96x96.png",
"sizes": "96x96",
"type": "image/png",
"purpose": "any"
},
{
"src": "/icons/icon-128x128.png",
"sizes": "128x128",
"type": "image/png",
"purpose": "any"
},
{
"src": "/icons/icon-144x144.png",
"sizes": "144x144",
"type": "image/png",
"purpose": "any"
},
{
"src": "/icons/icon-152x152.png",
"sizes": "152x152",
"type": "image/png",
"purpose": "any"
},
{
"src": "/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any"
},
{
"src": "/icons/icon-384x384.png",
"sizes": "384x384",
"type": "image/png",
"purpose": "any"
},
{
"src": "/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
}
]
}
const CACHE_NAME = 'pwa-app-cache-v1';
const ASSETS_TO_CACHE = [
'/','/index.html','/style.css','/script.js','/manifest.json',
'/icons/icon-72x72.png','/icons/icon-96x96.png','/icons/icon-128x128.png',
'/icons/icon-144x144.png','/icons/icon-152x152.png','/icons/icon-192x192.png',
'/icons/icon-384x384.png','/icons/icon-512x512.png'
];
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => {
console.log('Cache opened');
return cache.addAll(ASSETS_TO_CACHE);
})
.then(() => self.skipWaiting())
);
});
self.addEventListener('activate', event => {
const cacheWhitelist = [CACHE_NAME];
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (cacheWhitelist.indexOf(cacheName) === -1) {
return caches.delete(cacheName);
}
})
)
.then(() => self.clients.claim())
);
});
});
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(response => {
if (response) {
return response;
}
return fetch(event.request)
.then(response => {
if (response && response.status === 200 && response.type === 'basic') {
const responseToCache = response.clone();
caches.open(CACHE_NAME)
.then(cache => {
cache.put(event.request, responseToCache);
});
}
return response;
})
.catch(error => {
if (event.request.mode === 'navigate') {
return caches.match('/offline.html');
}
});
})
);
});
self.addEventListener('push', event => {
const data = event.data.json();
const options = {
body: data.body,
icon: '/icons/icon-192x192.png',
badge: '/icons/icon-72x72.png',
data: { url: data.url }
};
event.waitUntil(
self.registration.showNotification(data.title, options)
);
});
self.addEventListener('notificationclick', event => {
event.notification.close();
event.waitUntil(
clients.openWindow(event.notification.data.url)
);
});
function requestNotificationPermission() {
if ('Notification' in window) {
Notification.requestPermission().then(permission => {
if (permission === 'granted') {
console.log('通知权限已授予');
} else {
console.log('通知权限被拒绝');
}
});
}
}
function sendNotification() {
if ('serviceWorker' in navigator && 'PushManager' in window) {
navigator.serviceWorker.ready.then(registration => {
registration.showNotification('测试通知', {
body: '这是一条测试通知',
icon: '/icons/icon-192x192.png',
badge: '/icons/icon-72x72.png',
data: { url: '/' }
});
});
}
}
window.addEventListener('load', () => {
requestNotificationPermission();
const notificationBtn = document.getElementById('notification-btn');
if (notificationBtn) {
notificationBtn.addEventListener('click', sendNotification);
}
});
function checkPwaMode() {
const isPwa = window.matchMedia('(display-mode: standalone)').matches || window.navigator.standalone || document.referrer.includes('android-app://');
if (isPwa) {
console.log('应用以 PWA 模式运行');
} else {
console.log('应用以浏览器模式运行');
}
}
checkPwaMode();