本文目录导读:

我将为您详细介绍如何将PHP网站改造成PWA(渐进式Web应用)。
PWA核心组成部分
创建Manifest文件
创建 manifest.json:
{
"name": "我的PHP应用",
"short_name": "PHPApp",
"description": "这是一个PHP开发的PWA应用",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#4A90D9",
"orientation": "portrait",
"icons": [
{
"src": "/icons/icon-72x72.png",
"sizes": "72x72",
"type": "image/png"
},
{
"src": "/icons/icon-96x96.png",
"sizes": "96x96",
"type": "image/png"
},
{
"src": "/icons/icon-128x128.png",
"sizes": "128x128",
"type": "image/png"
},
{
"src": "/icons/icon-144x144.png",
"sizes": "144x144",
"type": "image/png"
},
{
"src": "/icons/icon-152x152.png",
"sizes": "152x152",
"type": "image/png"
},
{
"src": "/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icons/icon-384x384.png",
"sizes": "384x384",
"type": "image/png"
},
{
"src": "/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
创建Service Worker
创建 sw.js:
const CACHE_NAME = 'php-pwa-v1';
const urlsToCache = [
'/',
'/manifest.json',
'/css/styles.css',
'/js/main.js',
'/offline.html'
];
// 安装Service Worker
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => {
console.log('缓存已打开');
return cache.addAll(urlsToCache);
})
.then(() => self.skipWaiting())
);
});
// 激活Service Worker
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (cacheName !== CACHE_NAME) {
console.log('删除旧缓存', cacheName);
return caches.delete(cacheName);
}
})
);
})
);
});
// 处理API请求缓存策略
const cacheStrategies = {
// 缓存优先,用于静态资源
cacheFirst: async (request) => {
const cachedResponse = await caches.match(request);
if (cachedResponse) {
return cachedResponse;
}
try {
const networkResponse = await fetch(request);
if (networkResponse.status === 200) {
const cache = await caches.open(CACHE_NAME);
await cache.put(request, networkResponse.clone());
}
return networkResponse;
} catch (error) {
return caches.match('/offline.html');
}
},
// 网络优先,用于API接口
networkFirst: async (request) => {
try {
const networkResponse = await fetch(request);
if (networkResponse.status === 200) {
const cache = await caches.open(CACHE_NAME);
await cache.put(request, networkResponse.clone());
}
return networkResponse;
} catch (error) {
const cachedResponse = await caches.match(request);
return cachedResponse || caches.match('/offline.html');
}
},
// 仅网络
networkOnly: async (request) => {
return fetch(request);
},
// 仅缓存
cacheOnly: async (request) => {
return caches.match(request);
}
};
// 拦截请求
self.addEventListener('fetch', event => {
const url = new URL(event.request.url);
// 根据URL类型选择缓存策略
if (event.request.url.includes('/api/')) {
// API请求使用网络优先
event.respondWith(cacheStrategies.networkFirst(event.request));
} else if (event.request.mode === 'navigate') {
// 页面导航使用网络优先
event.respondWith(cacheStrategies.networkFirst(event.request));
} else if (event.request.url.includes('.php')) {
// PHP动态页面使用网络优先
event.respondWith(cacheStrategies.networkFirst(event.request));
} else {
// 静态资源使用缓存优先
event.respondWith(cacheStrategies.cacheFirst(event.request));
}
});
// 处理推送通知
self.addEventListener('push', event => {
const data = event.data.json();
const options = {
body: data.message,
icon: '/icons/icon-192x192.png',
badge: '/icons/icon-72x72.png',
vibrate: [200, 100, 200],
data: {
url: data.url || '/'
}
};
event.waitUntil(
self.registration.showNotification(data.title, options)
);
});
// 处理通知点击事件
self.addEventListener('notificationclick', event => {
event.notification.close();
event.waitUntil(
clients.matchAll({type: 'window'}).then(windowClients => {
// 检查是否有已打开的窗口
for (const client of windowClients) {
if (client.url === event.notification.data.url && 'focus' in client) {
return client.focus();
}
}
// 如果没有,则打开新窗口
if (clients.openWindow) {
return clients.openWindow(event.notification.data.url);
}
})
);
});
// 后台同步
self.addEventListener('sync', event => {
if (event.tag === 'sync-data') {
event.waitUntil(syncPendingData());
}
});
async function syncPendingData() {
// 在这里实现数据同步逻辑
console.log('同步后台数据');
}
PHP主页面修改
修改您的PHP主页面,添加PWA支持:
<?php
session_start();
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- PWA配置 -->
<link rel="manifest" href="/manifest.json">
<meta name="theme-color" content="#4A90D9">
<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="PHP App">
<!-- iOS相关 -->
<link rel="apple-touch-icon" href="/icons/icon-192x192.png">
PHP PWA应用</title>
<!-- 添加favicon -->
<link rel="icon" href="/icons/icon-96x96.png" type="image/png">
<!-- 引入样式和脚本 -->
<link rel="stylesheet" href="/css/styles.css">
<script src="/js/main.js" defer></script>
</head>
<body>
<header>
<!-- 导航部分 -->
</header>
<main>
<h1>欢迎使用PHP PWA</h1>
<p>这是一个支持离线访问的PHP应用</p>
<!-- 内容区域 -->
<div id="content">
<?php
// PHP内容输出
echo "<p>当前时间: " . date('Y-m-d H:i:s') . "</p>";
?>
</div>
</main>
<footer>
<?php // 页脚内容 ?>
</footer>
<script>
// 注册Service Worker
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/sw.js')
.then(function(registration) {
console.log('Service Worker注册成功:', registration.scope);
})
.catch(function(error) {
console.log('Service Worker注册失败:', error);
});
});
}
</script>
</body>
</html>
PHP后端代码优化
创建一个专门的PHP文件处理API请求:
// api.php
<?php
header('Content-Type: application/json');
header('Cache-Control: no-cache, no-store, must-revalidate');
header('Pragma: no-cache');
header('Expires: 0');
// 处理CORS
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE');
header('Access-Control-Allow-Headers: Content-Type');
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
exit(0);
}
// API逻辑
$action = $_GET['action'] ?? '';
switch ($action) {
case 'data':
$data = ['status' => 'success', 'message' => 'API数据', 'time' => date('Y-m-d H:i:s')];
echo json_encode($data);
break;
case 'user':
// 处理用户请求
$userData = [
'id' => 1,
'name' => '测试用户',
'email' => 'user@example.com'
];
echo json_encode($userData);
break;
default:
http_response_code(404);
echo json_encode(['status' => 'error', 'message' => '接口不存在']);
}
离线页面
创建 offline.html:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">离线页面</title>
<style>
body {
font-family: Arial, sans-serif;
text-align: center;
padding: 50px;
background: #f5f5f5;
color: #333;
}
.offline-icon {
font-size: 100px;
margin-bottom: 20px;
}
.retry-button {
background: #4A90D9;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
}
</style>
</head>
<body>
<div class="offline-icon">📡</div>
<h1>您似乎处于离线状态</h1>
<p>请检查您的网络连接</p>
<button class="retry-button" onclick="window.location.reload()">重试</button>
</body>
</html>
推送通知功能
创建PHP推送通知结合Web Push:
// push_notification.php
<?php
require_once 'vendor/autoload.php'; // 假设使用web-push-php库
use Minishlink\WebPush\WebPush;
use Minishlink\WebPush\Subscription;
$auth = [
'VAPID' => [
'subject' => 'mailto:your-email@example.com',
'publicKey' => 'your-public-key',
'privateKey' => 'your-private-key'
]
];
$webPush = new WebPush($auth);
// 发送推送通知
function sendPushNotification($endpoint, $title, $message, $url) {
global $webPush;
$subscription = Subscription::create([
'endpoint' => $endpoint,
'keys' => [
'p256dh' => 'user-p256dh-key',
'auth' => 'user-auth-key'
]
]);
$payload = json_encode([
'title' => $title,
'message' => $message,
'url' => $url
]);
$webPush->queueNotification($subscription, $payload);
foreach ($webPush->flush() as $report) {
if (!$report->isSuccess()) {
// 处理失败
error_log("推送通知失败: " . $report->getReason());
}
}
}
// 接收订阅信息
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['subscribe'])) {
$subscriptionData = json_decode($_POST['subscription'], true);
// 保存订阅信息到数据库
saveSubscriptionToDatabase($subscriptionData);
}
function saveSubscriptionToDatabase($data) {
// 实现数据库保存逻辑
}
// 示例:发送测试通知
if (isset($_GET['test'])) {
sendPushNotification(
'user-endpoint',
'测试通知',
'这是一条测试推送通知',
'/'
);
}
前端推送通知支持
前端JavaScript处理推送:
// 处理推送通知
async function setupPushNotifications() {
// 检查浏览器是否支持
if (!('Notification' in window)) {
console.log('此浏览器不支持通知');
return;
}
// 检查是否支持Service Worker
if (!('serviceWorker' in navigator)) {
return;
}
// 请求权限
const permission = await Notification.requestPermission();
if (permission === 'granted') {
const registration = await navigator.serviceWorker.ready;
// 获取订阅
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array('your-vapid-public-key')
});
// 发送到服务器
fetch('/push_notification.php', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `subscribe=1&subscription=${JSON.stringify(subscription)}`
});
}
}
function urlBase64ToUint8Array(base64String) {
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding)
.replace(/\-/g, '+')
.replace(/_/g, '/');
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
// 页面加载时设置
document.addEventListener('DOMContentLoaded', function() {
setupPushNotifications();
});
测试和部署
测试清单
- 本地测试:使用HTTPS或在localhost测试
- 检查Lighthouse:使用Chrome DevTools的Lighthouse工具
- 响应式测试:确保在不同设备上正常运行
性能优化建议
// 在PHP中添加缓存头
<?php
// 静态资源缓存
header('Cache-Control: public, max-age=31536000');
// 动态页面控制缓存
header('Cache-Control: no-cache, must-revalidate');
header('Expires: Sat, 26 Jul 1997 05:00:00 GMT');
?>
```
### 安全注意事项
- 始终使用HTTPS
- 定期更新Service Worker
- 处理用户数据的隐私问题
- 合理管理缓存清理策略
这样,您就完成了一个完整的PHP PWA应用,PWA的核心是提供app-like的用户体验,同时保持Web的开放性和可访问性。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播