Files
Yep_Q 3825deb3e0 优化项目配置和页面结构
- 更新settings.local.json,移除冗余的权限设置,添加新的Playwright相关权限。
- 删除exhibition_demo_project_2025.md文档,清理不再使用的文件。
- 更新多个HTML页面,统一viewport设置,添加页面加载动画、错误处理和性能优化脚本。
- 统一使用Tailwind CSS的引入方式,提升页面加载性能。
- 增强导航组件,支持移动端菜单和返回顶部功能,改善用户体验。
2025-09-10 02:35:16 +08:00

283 lines
7.8 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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 || '/')
);
});