优化项目配置和页面结构
- 更新settings.local.json,移除冗余的权限设置,添加新的Playwright相关权限。 - 删除exhibition_demo_project_2025.md文档,清理不再使用的文件。 - 更新多个HTML页面,统一viewport设置,添加页面加载动画、错误处理和性能优化脚本。 - 统一使用Tailwind CSS的引入方式,提升页面加载性能。 - 增强导航组件,支持移动端菜单和返回顶部功能,改善用户体验。
This commit is contained in:
283
web_frontend/web_result/sw.js
Normal file
283
web_frontend/web_result/sw.js
Normal file
@@ -0,0 +1,283 @@
|
||||
// Service Worker for performance optimization
|
||||
const CACHE_NAME = 'n8n-demo-v1';
|
||||
const STATIC_CACHE = 'static-v1';
|
||||
const DYNAMIC_CACHE = 'dynamic-v1';
|
||||
|
||||
// 需要缓存的静态资源
|
||||
const STATIC_FILES = [
|
||||
'/',
|
||||
'/index.html',
|
||||
'/pages/overview.html',
|
||||
'/pages/exhibition.html',
|
||||
'/pages/marketing.html',
|
||||
'/pages/operation.html',
|
||||
'/pages/budget.html',
|
||||
'/pages/risk.html',
|
||||
'/js/nav-component.js',
|
||||
'/js/back-to-top.js',
|
||||
'/js/mobile-optimize.js',
|
||||
'/js/performance-optimizer.js',
|
||||
'/js/page-loader.js',
|
||||
'/css/styles.css',
|
||||
'/css/animations.css'
|
||||
];
|
||||
|
||||
// 缓存策略配置
|
||||
const CACHE_STRATEGIES = {
|
||||
// 静态资源:缓存优先
|
||||
static: {
|
||||
pattern: /\.(js|css|woff|woff2|ttf|eot)$/,
|
||||
strategy: 'cacheFirst',
|
||||
maxAge: 30 * 24 * 60 * 60 * 1000 // 30天
|
||||
},
|
||||
// HTML页面:网络优先,失败时使用缓存
|
||||
html: {
|
||||
pattern: /\.html$/,
|
||||
strategy: 'networkFirst',
|
||||
maxAge: 24 * 60 * 60 * 1000 // 1天
|
||||
},
|
||||
// 图片:缓存优先
|
||||
images: {
|
||||
pattern: /\.(png|jpg|jpeg|gif|svg|webp)$/,
|
||||
strategy: 'cacheFirst',
|
||||
maxAge: 7 * 24 * 60 * 60 * 1000 // 7天
|
||||
},
|
||||
// 外部资源:网络优先
|
||||
external: {
|
||||
pattern: /^https:\/\/(fonts\.googleapis\.com|cdnjs\.cloudflare\.com|cdn\.jsdelivr\.net)/,
|
||||
strategy: 'networkFirst',
|
||||
maxAge: 7 * 24 * 60 * 60 * 1000 // 7天
|
||||
}
|
||||
};
|
||||
|
||||
// 安装事件 - 缓存静态资源
|
||||
self.addEventListener('install', event => {
|
||||
console.log('SW: Install event');
|
||||
|
||||
event.waitUntil(
|
||||
caches.open(STATIC_CACHE)
|
||||
.then(cache => {
|
||||
console.log('SW: Caching static files');
|
||||
return cache.addAll(STATIC_FILES);
|
||||
})
|
||||
.then(() => {
|
||||
console.log('SW: Static files cached');
|
||||
return self.skipWaiting();
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('SW: Failed to cache static files', err);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
// 激活事件 - 清理旧缓存
|
||||
self.addEventListener('activate', event => {
|
||||
console.log('SW: Activate event');
|
||||
|
||||
event.waitUntil(
|
||||
caches.keys()
|
||||
.then(cacheNames => {
|
||||
return Promise.all(
|
||||
cacheNames.map(cacheName => {
|
||||
// 删除旧版本的缓存
|
||||
if (cacheName !== STATIC_CACHE && cacheName !== DYNAMIC_CACHE) {
|
||||
console.log('SW: Deleting old cache:', cacheName);
|
||||
return caches.delete(cacheName);
|
||||
}
|
||||
})
|
||||
);
|
||||
})
|
||||
.then(() => {
|
||||
console.log('SW: Old caches cleaned');
|
||||
return self.clients.claim();
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
// 获取事件 - 实现缓存策略
|
||||
self.addEventListener('fetch', event => {
|
||||
// 只处理 GET 请求
|
||||
if (event.request.method !== 'GET') {
|
||||
return;
|
||||
}
|
||||
|
||||
const url = new URL(event.request.url);
|
||||
|
||||
// 跳过 chrome-extension 等协议
|
||||
if (!url.protocol.startsWith('http')) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.respondWith(handleFetch(event.request));
|
||||
});
|
||||
|
||||
// 处理请求的核心函数
|
||||
async function handleFetch(request) {
|
||||
const url = new URL(request.url);
|
||||
|
||||
try {
|
||||
// 确定缓存策略
|
||||
const strategy = getCacheStrategy(request);
|
||||
|
||||
switch (strategy.strategy) {
|
||||
case 'cacheFirst':
|
||||
return await cacheFirst(request, strategy);
|
||||
case 'networkFirst':
|
||||
return await networkFirst(request, strategy);
|
||||
case 'networkOnly':
|
||||
return await fetch(request);
|
||||
default:
|
||||
return await networkFirst(request, strategy);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('SW: Fetch failed:', error);
|
||||
|
||||
// 返回离线页面或缓存的响应
|
||||
return await getOfflineResponse(request);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取缓存策略
|
||||
function getCacheStrategy(request) {
|
||||
const url = request.url;
|
||||
|
||||
for (const [key, config] of Object.entries(CACHE_STRATEGIES)) {
|
||||
if (config.pattern.test(url)) {
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
||||
// 默认策略
|
||||
return { strategy: 'networkFirst', maxAge: 24 * 60 * 60 * 1000 };
|
||||
}
|
||||
|
||||
// 缓存优先策略
|
||||
async function cacheFirst(request, strategy) {
|
||||
const cacheName = isStaticResource(request) ? STATIC_CACHE : DYNAMIC_CACHE;
|
||||
const cache = await caches.open(cacheName);
|
||||
const cachedResponse = await cache.match(request);
|
||||
|
||||
if (cachedResponse) {
|
||||
// 检查缓存是否过期
|
||||
const cachedDate = new Date(cachedResponse.headers.get('date') || Date.now());
|
||||
const now = new Date();
|
||||
|
||||
if (now - cachedDate < strategy.maxAge) {
|
||||
return cachedResponse;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const networkResponse = await fetch(request);
|
||||
|
||||
if (networkResponse.ok) {
|
||||
// 缓存新响应
|
||||
await cache.put(request, networkResponse.clone());
|
||||
}
|
||||
|
||||
return networkResponse;
|
||||
} catch (error) {
|
||||
// 网络失败,返回缓存(即使过期)
|
||||
if (cachedResponse) {
|
||||
return cachedResponse;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 网络优先策略
|
||||
async function networkFirst(request, strategy) {
|
||||
const cacheName = isStaticResource(request) ? STATIC_CACHE : DYNAMIC_CACHE;
|
||||
const cache = await caches.open(cacheName);
|
||||
|
||||
try {
|
||||
const networkResponse = await fetch(request);
|
||||
|
||||
if (networkResponse.ok) {
|
||||
// 缓存响应
|
||||
await cache.put(request, networkResponse.clone());
|
||||
}
|
||||
|
||||
return networkResponse;
|
||||
} catch (error) {
|
||||
// 网络失败,尝试从缓存获取
|
||||
const cachedResponse = await cache.match(request);
|
||||
|
||||
if (cachedResponse) {
|
||||
return cachedResponse;
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 判断是否为静态资源
|
||||
function isStaticResource(request) {
|
||||
const url = new URL(request.url);
|
||||
return STATIC_FILES.some(file => url.pathname.endsWith(file));
|
||||
}
|
||||
|
||||
// 获取离线响应
|
||||
async function getOfflineResponse(request) {
|
||||
const url = new URL(request.url);
|
||||
|
||||
// 如果是 HTML 请求,返回主页
|
||||
if (request.headers.get('accept')?.includes('text/html')) {
|
||||
const cache = await caches.open(STATIC_CACHE);
|
||||
return await cache.match('/index.html') || new Response('Offline', { status: 503 });
|
||||
}
|
||||
|
||||
// 其他资源返回 503
|
||||
return new Response('Offline', { status: 503 });
|
||||
}
|
||||
|
||||
// 监听消息事件
|
||||
self.addEventListener('message', event => {
|
||||
if (event.data && event.data.type === 'SKIP_WAITING') {
|
||||
self.skipWaiting();
|
||||
}
|
||||
|
||||
if (event.data && event.data.type === 'GET_VERSION') {
|
||||
event.ports[0].postMessage({ version: CACHE_NAME });
|
||||
}
|
||||
});
|
||||
|
||||
// 后台同步(如果支持)
|
||||
self.addEventListener('sync', event => {
|
||||
if (event.tag === 'background-sync') {
|
||||
event.waitUntil(doBackgroundSync());
|
||||
}
|
||||
});
|
||||
|
||||
async function doBackgroundSync() {
|
||||
// 这里可以执行后台同步任务
|
||||
console.log('SW: Background sync');
|
||||
}
|
||||
|
||||
// 推送通知(如果需要)
|
||||
self.addEventListener('push', event => {
|
||||
if (event.data) {
|
||||
const data = event.data.json();
|
||||
const options = {
|
||||
body: data.body,
|
||||
icon: data.icon || '/favicon.ico',
|
||||
badge: '/badge.png',
|
||||
vibrate: [200, 100, 200],
|
||||
data: data.data,
|
||||
actions: data.actions
|
||||
};
|
||||
|
||||
event.waitUntil(
|
||||
self.registration.showNotification(data.title, options)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// 通知点击事件
|
||||
self.addEventListener('notificationclick', event => {
|
||||
event.notification.close();
|
||||
|
||||
event.waitUntil(
|
||||
clients.openWindow(event.notification.data?.url || '/')
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user